PageRenderTime 64ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/docs/yolk.txt

http://github.com/ThomasLocke/yolk
Plain Text | 2182 lines | 1296 code | 886 blank | 0 comment | 0 complexity | 40f173b56532aabd2b6c1e4535d4b6f7 MD5 | raw file
Possible License(s): AGPL-1.0
  1. Yolk Manual
  2. Revised December 11th. 2012
  3. Table of Contents
  4. General Information
  5. 1 Copyright and License
  6. This document is copyright (C) 2010-, Thomas Løcke. You may copy
  7. this document, in whole or in part, in any form or by any means,
  8. as is or with alterations, provided that (1) alterations are
  9. clearly marked as alterations and (2) this copyright notice is
  10. included unmodified in any copy.
  11. Yolk is GPLv3 software. You should have received a copy of the
  12. GNU General Public License and a copy of the GCC Runtime Library
  13. Exception along with this program; see the files COPYING3 and
  14. COPYING.RUNTIME respectively. If not, see [http://www.gnu.org/licenses/||http://www.gnu.org/licenses/]
  15. .
  16. 2 What is Yolk?
  17. Yolk is a collection of packages that aim to help build solid
  18. web-applications using Ada. Yolk itself doesn't do a whole lot
  19. that can't be accomplished simply by using [http://libre.adacore.com/libre/tools/aws/||AWS]
  20. and the [http://libre.adacore.com/libre/tools/gnat-component-collection/||GNAT Component Collection (GNATcoll)]
  21. , but it does make the job of building complete web-applications
  22. a bit simpler. Things like changing user for the running
  23. application, catching POSIX signals such as SIGKILL, sending log
  24. data to syslogd, adding basic static content handlers, creating
  25. and starting/stopping an AWS powered HTTP server and building
  26. Atom syndication XML are all made a bit easier with Yolk.
  27. A Yolk application is in reality an AWS application, with some
  28. sugar added, so you're not really building a Yolk
  29. web-application, as much as you're building an AWS
  30. web-application. What I'm getting at, is that you need to
  31. understand how to use AWS, in order for Yolk to make any kind of
  32. sense. What you get when using Yolk is the little things that AWS
  33. does not readily provide.
  34. 2.1 The Yolk demo application
  35. Reading this manual will of course (I hope!) help you understand
  36. how to use Yolk, but please consider taking a closer look at the
  37. Yolk demo application to get a feel for how Yolk is actually
  38. used. The demo is heavily commented, so it should be fairly easy
  39. to understand what's going on. The demo application is also very
  40. suitable as a foundation for other AWS/Yolk applications.
  41. It is much easier to show how to use Yolk, than it is to write
  42. down all possible usage scenarios. With the combination of this
  43. manual, the Yolk source files and the demo application, you
  44. should be able to make full use of the Yolk packages in your own
  45. applications.
  46. 2.2 The source code
  47. The Yolk source code is the best documentation there is. This
  48. document is never going to be as comprehensive as the actual
  49. source, so I'll strongly suggest having the source code available
  50. as you read this document. What you will find in this document
  51. are short descriptions of what a package is meant to do and
  52. perhaps small usage examples, not a complete rundown of every
  53. type and procedure in a package.
  54. 2.3 Building and installing Yolk
  55. See the README and INSTALL files. These are found in the Yolk
  56. root directory.
  57. 2.4 The files Yolk depend upon
  58. When you read this document and the Yolk source code, you'll
  59. notice that quite a few packages depend on various files being
  60. available at specified locations. This is for example the case
  61. with the Yolk.Whoops package that expects its template file to be
  62. found at the path templates/system/500.tmpl
  63. All such dependencies will of course be noted accordingly as we
  64. go along, but instead of forgetting one or more in your own
  65. application, I'd much rather encourage using the demo application
  66. as a foundation for your own applications, since all these fixed
  67. paths and files has been properly added to the demo.
  68. I also recommend compiling and running the demo, to make sure
  69. your Yolk install is working as intended. Just read the
  70. demo/README and demo/INSTALL files for instructions on how to get
  71. it up and running.
  72. 2.5 The Yolk packages naming
  73. The Yolk packages are pretty diverse, ranging from process
  74. control to sending email. I've tried naming them as sensibly as
  75. possible, in the hope that the package names alone give away
  76. their function. If I've failed, well, you're just going to have
  77. to refer to this document or take a look at the source for
  78. yourself.
  79. The Yolk Packages
  80. 3 Yolk
  81. The Yolk main package currently only contain only a few things:
  82. The Yolk Version string, a Config_File function to get the
  83. location of the configuration file and a PID_File function to get
  84. the location of the PID file. These are used in a few places, for
  85. example in the directory.tmpl template file (the version string),
  86. in the Yolk.Configuration package (the Config_File function) and
  87. in the Yolk.Process_Control package (the PID_File function).
  88. All Yolk applications accepts two commandline arguments:
  89. --yolk-config-file : Defines the location of the configuration
  90. file. If empty or not set, then use the default location
  91. configuration/config.ini.
  92. --pid-file : Defines the location of the PID file. If empty or
  93. not set, then don't write a PID file. Note that if you use the
  94. extras/rc.yolk script to control your application, then this is
  95. handled for you transparently.
  96. 4 Yolk.Cache.Discrete_Keys
  97. If a piece of data doesn't change very often and it is expensive
  98. to build, then caching it might be worthwhile. Instead of going
  99. to a file or database on every hit, you simply go to the cache
  100. and grab the latest version from there. This is very fast, at the
  101. cost of some memory.
  102. If you know exactly what you want to cache, the
  103. Yolk.Cache.Discrete_Keys package might be just what you need.
  104. 4.1 The generic formal parameters
  105. These are:
  106. generic
  107. type Key_Type is (<>);
  108. type Element_Type is private;
  109. Max_Element_Age : Duration := 3600.0;
  110. package Yolk.Cache.Discrete_Keys is
  111. ...
  112. The Max_Element_Age defaults to one hour. You should obviously
  113. set this to whatever suits your needs. This timer is used for all
  114. content in the cache. You cannot set this individually for each
  115. element.
  116. 4.2 Instantiation
  117. If for example we have two different sets of data (Foo and Bar)
  118. that are expensive to build, we can instantiate a Discrete_Keys
  119. package to handle this:
  120. type Cache_Keys is (Foo, Bar);
  121. package My_Cache is new Yolk.Cache.Discrete_Keys
  122. (Key_Type => Cache_Keys,
  123. Element_Type => Unbounded_String);
  124. And that is all. We now have a My_Cache object that can hold two
  125. objects: Foo and Bar. These are of the type Unbounded_String and
  126. they have a Max_Element_Age of 3600.0 seconds.
  127. 4.3 Writing to the cache
  128. Before we can read something from the cache, we must first write
  129. something to it:
  130. declare
  131. Foo_Value : Unbounded_String := To_Unbounded_String ("Foo");
  132. begin
  133. My_Cache.Write (Key => Foo,
  134. Value => Foo_Value);
  135. end;
  136. That is all it takes: Foo is now safely tucked away in the
  137. My_Cache object, and will be so for 3600.0 seconds. Calling Write
  138. with the Foo key will always overwrite earlier written Foo
  139. elements, no matter their age.
  140. 4.4 Reading from the cache
  141. A cache obviously only makes sense if you intend to read from it.
  142. In our case we want to get our hands on the previously written
  143. Foo value:
  144. declare
  145. Valid : Boolean := False;
  146. Value : Unbounded_String;
  147. begin
  148. My_Cache.Read (Key => Foo,
  149. Is_Valid => Valid,
  150. Value => Value);
  151. if Valid then
  152. -- do something interesting with the data
  153. else
  154. -- the Foo data is invalid.
  155. end if;
  156. end;
  157. In order for an element to be valid (the Is_Valid parameter is
  158. true), it must:
  159. 1. have been added to the cache in the first place
  160. 2. be younger than Max_Element_Age
  161. If Is_Valid is False, then Value is undefined. Note that if
  162. Is_Valid is False then Key is removed from the cache, if it
  163. exists.
  164. 4.5 Checking if a key is valid
  165. If you need to check whether a specific key exists in the cache
  166. and is valid, then you must use the Is_Valid function.
  167. if My_Cache.Is_Valid (Foo) then
  168. -- Foo is good!
  169. else
  170. -- Foo is bad!
  171. end if;
  172. This follows the same rules as the Is_Valid parameter for the
  173. Read procedure.
  174. 4.6 Clearing keys and the entire cache
  175. For clearing of keys and the entire cache we have, naturally, two
  176. Clear procedures:
  177. -- First we clear the Foo key
  178. My_Cache.Clear (Key => Foo);
  179. -- And then we clear the entire cache
  180. My_Cache.Clear;
  181. And that's all it takes.
  182. 4.7 Cleanup - Getting rid of stale elements
  183. Calling Cleanup will delete all stale elements from the cache:
  184. My_Cache.Cleanup;
  185. Note that this is potentially a very expensive operation if the
  186. cache is large, as the entire cache is iterated and every element
  187. tested for its age. Use with care.
  188. 5 Yolk.Cache.String_Keys
  189. This package is almost similar to the Yolk.Cache.Discrete_Keys
  190. package. The biggest difference is that where the Discrete_Keys
  191. cache package requires that you define a type for the keys, this
  192. package use regular String as keys.
  193. The implications of this difference between the two cache
  194. packages are subtle. Both have the same Read, Write, Is_Valid and
  195. Clear procedures and functions, so in that sense the two packages
  196. are the same. The biggest difference lies in the available
  197. generic formal parameters and the functionality of the Cleanup
  198. procedure.
  199. 5.1 The generic formal parameters
  200. These are:
  201. generic
  202. type Element_Type is private;
  203. Cleanup_Size : Positive := 200;
  204. Cleanup_On_Write : Boolean := True;
  205. Max_Element_Age : Duration := 3600.0;
  206. Reserved_Capacity : Positive := 100;
  207. package Yolk.Cache.Discrete_Keys is
  208. ...
  209. When the amount of elements in the cache >= Cleanup_Size, then
  210. the Cleanup procedure is called by Write, if Cleanup_On_Write is
  211. set to Boolean True. Cleanup_Size is a sort of failsafe for this
  212. cache package. Since we can't know for sure what is being added
  213. (we don't know the keys beforehand), we need to make sure it
  214. doesn't gobble up all available resources. Set this number high
  215. enough that it'll never tricker under normal circumstances, but
  216. low enough that it'll prevent resource exhaustion in case of
  217. errors.
  218. The Max_Element_Age defaults to one hour. You should obviously
  219. set this to whatever suits your needs. This timer is used for all
  220. content in the cache. You cannot set this individually for each
  221. element.
  222. Reserved_Capacity should be set as close as possible to the
  223. expected final size of the cache. If your best guestimate is 200
  224. elements in the cache, then set this to 200. Note that this
  225. setting has no bearing on the actual size of the cache. The cache
  226. will happily grow beyond the Reserved_Capacity value.
  227. 5.2 Instantiation
  228. Instantiating String_Keys is done like this:
  229. package My_Cache is new Yolk.Cache.String_Keys
  230. (Element_Type => Unbounded_String,
  231. Reserved_Capacity => 200);
  232. And that is all. We now have a My_Cache object that can hold
  233. objects of the type Unbounded_String, all of which have a
  234. Max_Element_Age of 3600.0 seconds. Also we've told the cache to
  235. set aside at least 200 positions for content.
  236. 5.3 Writing to the cache
  237. Before we can read something from the cache, we must first write
  238. something to it:
  239. declare
  240. Value : Unbounded_String := To_Unbounded_String ("42");
  241. begin
  242. My_Cache.Write (Key => "Foo",
  243. Value => Value);
  244. end;
  245. 42 is now safely tucked away in the My_Cache object under the
  246. key Foo, and will be so for 3600.0 seconds. Calling Write with
  247. the Foo String will always overwrite earlier written Foo
  248. elements, no matter their age.
  249. 5.4 Reading from the cache
  250. A cache obviously only makes sense if you intend to read from it.
  251. In our case we want to get our hands on the previously written
  252. Foo value:
  253. declare
  254. Valid : Boolean := False;
  255. Value : Unbounded_String;
  256. begin
  257. My_Cache.Read (Key => "Foo",
  258. Is_Valid => Valid,
  259. Value => Value);
  260. if Valid then
  261. -- do something interesting with the data
  262. else
  263. -- the Foo data is invalid.
  264. end if;
  265. end;
  266. In order for an element to be valid (the Is_Valid parameter is
  267. true), it must:
  268. 1. have been added to the cache in the first place
  269. 2. be younger than Max_Element_Age
  270. If Is_Valid is False, then Value contains undefined garbage. Note
  271. that if Is_Valid is False then Key is removed from the cache, if
  272. it exists.
  273. 5.5 Checking if a key is valid
  274. If you need to check whether a specific key exists in the cache
  275. and is valid, then you need to use the Is_Valid function.
  276. if My_Cache.Is_Valid ("Foo") then
  277. -- Foo is good!
  278. else
  279. -- Foo is bad!
  280. end if;
  281. This follows the same rules as the Is_Valid parameter for the
  282. Read procedure.
  283. 5.6 Clearing keys and the entire cache
  284. For clearing of keys and the entire cache we have, naturally, two
  285. Clear procedures:
  286. -- First we clear the Foo key
  287. My_Cache.Clear (Key => "Foo");
  288. -- And then we clear the entire cache
  289. My_Cache.Clear;
  290. 5.7 How much is in there?
  291. With the Discrete_Keys cache we obviously always know the exact
  292. amount of keys available, since we've defined the keys ourselves.
  293. This is not the case with the String_Keys cache, where any String
  294. can be a key. If we need to know how many elements that are
  295. currently in the cache, we call the Length function:
  296. if My_Cache.Length > 1000 then
  297. -- Woa! Lots of stuff in the cache..
  298. end if;
  299. Note that Length count both valid and invalid elements.
  300. 5.8 Cleanup - Keeping cache size in check
  301. if Cleanup_On_Write is True, then Cleanup is called by Write
  302. whenever the size of the cache reach Cleanup_Size. It is of
  303. course also possible to call it manually:
  304. if My_Cache.Length > 1000 then
  305. My_Cache.Cleanup;
  306. end if;
  307. If you've set Cleanup_On_Write to Boolean False and the String
  308. keys are coming from outside sources, then you really should make
  309. sure you call Cleanup on a regular basis.
  310. 6 Yolk.Command_Line
  311. This package enables you to fetch the value of a given
  312. commandline parameter using the Get function:
  313. function Get
  314. (Parameter : in String;
  315. Default : in String := "")
  316. return String;
  317. If Parameter is found Get will return the value immediately
  318. following Parameter. If Parameter isn't found Default is
  319. returned.
  320. 7 Yolk.Config_File_Parser
  321. This package enables you to access KEY/VALUE pairs in
  322. configuration files that are written in the style:
  323. # This is a comment
  324. -- This is also a comment
  325. KEY VALUE
  326. Keys are case-insensitive, so FOO, foo and fOo are all the same.
  327. Blank lines and comments are ignored and so is pre/postfixed
  328. whitespace. It is not necessary to quote values that contain
  329. whitespace, to this:
  330. KEY some value with whitespace
  331. is perfectly valid, and will return some value with whitespace
  332. when calling Get (KEY). If VALUE is Boolean True or False
  333. (case-insensitive), then the KEY can be returned as a String or a
  334. Boolean, depending on the target type. If the target type does
  335. not match the VALUE and no sensible conversion can be made, then
  336. a Conversion_Error exception is raised. No dummy values are
  337. returned at any time.
  338. To clear a default value, simply add the key to the configuration
  339. file, with no value set.
  340. 7.1 The generic formal parameters
  341. These are:
  342. generic
  343. use Ada.Strings.Unbounded;
  344. type Key_Type is (<>);
  345. type Defaults_Array_Type is array (Key_Type) of
  346. Unbounded_String;
  347. Defaults : in Defaults_Array_Type;
  348. Config_File : in String;
  349. package Yolk.Config_File_Parser is
  350. ...
  351. Config_File is of course the name and location of the
  352. configuration file.
  353. 7.2 Exceptions
  354. There are 3 different exceptions that can be raised by the
  355. Yolk.Config_File_Parser package. These are:
  356. Unknown_Key. This is raised if an unknown key has been found in
  357. the configuration file given when instantiating the package or
  358. when Load_File is called.
  359. Cannot_Open_Config_File. This is raised when Config_File cannot
  360. be read.
  361. Conversion_Error. This is raised when a value cannot be
  362. converted to the target type, ie. the value 42 to a Boolean.
  363. 7.3 Instantiation
  364. Yolk.Config_File_Parser is a generic package, so in order to use
  365. it, you have to instantiate it, like this:
  366. package My_Configuration is
  367. type Keys is (Foo, Bar);
  368. type Defaults_Array is array (Keys) of Unbounded_String;
  369. Default_Values : constant Defaults_Array :=
  370. (Foo => To_Unbounded_String ("some foo"),
  371. Bar => To_Unbounded_String ("some bar"));
  372. package Config is new Yolk.Config_File_Parser
  373. (Key_Type => Keys,
  374. Defaults_Array_Type => Defaults_Array,
  375. Defaults => Default_Value,
  376. Config_File => "config.ini");
  377. end My_Configuration;
  378. Here we instantiate the Config package with config.ini as the
  379. configuration file. This means that KEY/VALUE pairs found in this
  380. file will overwrite the default values set in the Default_Values
  381. array. Setting a default value to Null_Unbounded_String means the
  382. value is empty.
  383. Note that the config.ini file does not have to contain all the
  384. valid keys. It is perfectly fine to only add those keys that have
  385. non-default values to the configuration file.
  386. 7.4 Re-loading configuration files
  387. With the Load_File procedure you can re-load a new configuration
  388. file into your Config package:
  389. My_Configuration.Config.Load_File ("new_config.ini");
  390. Now the KEY/VALUE pairs of new_config.ini will overwrite the ones
  391. originally found in the config.ini file the package was
  392. instantiated with. You can do this as many times as you like.
  393. Note that you cannot change what KEY's are valid, so if the
  394. new_config.ini file contains unknown keys, Load_File will raise
  395. the Unknown_Key exception.
  396. 7.5 Getting values
  397. With instantiation and loading of configuration files out of the
  398. way, it is now time to get to the configuration values. To get
  399. the value of the Foo key, you do:
  400. My_Configuration.Config.Get (Foo);
  401. There are Get functions for the following types:
  402. Boolean
  403. Duration
  404. Float
  405. Integer
  406. String
  407. Unbounded_String
  408. Empty keys simply return an empty String or a
  409. Null_Unbounded_String, depending on the target type. If a key is
  410. empty and the target type is not a String or an Unbounded_String,
  411. then the Conversion_Error exception is raised.
  412. 7.6 Checking if a KEY has a VALUE
  413. You can check if a key has a value with the Has_Value function:
  414. if Has_Value (Foo) then
  415. Put_Line ("Foo has a value");
  416. end if;
  417. Basically all this function does is return Boolean True if the
  418. value of the given key is not a Null_Unbounded_String.
  419. 8 Yolk.Configuration
  420. This package is a bit of an oddball, as all it does is
  421. instantiate the Yolk.Config_File_Parser generic with the default
  422. AWS and Yolk configuration values. This is used by Yolk
  423. internally, but also by the AWS component of your application.
  424. The instantiation looks like this:
  425. package Config is new Config_File_Parser
  426. (Key_Type => Keys,
  427. Defaults_Array_Type => Defaults_Array,
  428. Defaults => Default_Values,
  429. Config_File => Config_File);
  430. The Config_File function call return either the default Yolk
  431. configuration file (configuration/config.ini) or a user specified
  432. configuration file given by the --yolk-config-file command line
  433. argument, so starting for example the Yolk demo like this:
  434. ./yolk_demo --yolk-config-file /etc/yolk-config.ini
  435. will force the demo to look for the /etc/yolk-config.ini
  436. configuration file.
  437. There's a fully commented config.ini.dist file available in the
  438. extras/ directory. I recommend taking a look at the Yolk demo
  439. application to see how the Yolk.Configuration package is used.
  440. 8.1 Get the AWS specific configuration settings
  441. On some occassions it might be necessary to get the AWS
  442. configuration object. This can easily be accomplished by calling
  443. the Get_AWS_Configuration function:
  444. AWS_Config : constant AWS.Config.Object :=
  445. Yolk.Configuration.Get_AWS_Configuration;
  446. 9 Yolk.Email
  447. Using Yolk.Email and the child package Yolk.Email.Composer you
  448. can build and send more or less any kind of email:
  449. Plain text
  450. Multipart/Alternative
  451. Multipart/Mixed
  452. The package supports adding multiple SMTP servers, meaning you
  453. can add as many as you need, and the email will then be send via
  454. the first one that accepts it.
  455. The Yolk.Email package define 4 exceptions and 3 types. The
  456. facilities for actually constructing and sending the email are
  457. found in Yolk.Email.Composer.
  458. 9.1 Exceptions
  459. These are:
  460. Attachment_File_Not_Found. Is raised if a file attachment is
  461. not found at the given path.
  462. No_Address_Set. Is raised if the address component of a To,
  463. Reply-To, From, Bcc/Cc header is missing.
  464. No_Sender_Set_With_Multiple_From. Is raised when an email
  465. contains multiple From headers but no Sender header, as per
  466. RFC-5322, 3.6.2. http://tools.ietf.org/html/rfc5322
  467. No_SMTP_Host_Set. Is raised if the SMTP host list is empty, ie.
  468. no SMTP host has been set for sending the email.
  469. 9.2 The Yolk.Email types
  470. When using Yolk.Email.Composer to build and send emails, three
  471. types declared in Yolk.Email are central:
  472. 1. Character_Set
  473. 2. Recipient_Kind
  474. 3. Structure
  475. The Character_Set type define what character set is used when
  476. data is added to an email Structure object. For example looking
  477. at the Yolk.Email.Composer.Add_From procedure, we see that the
  478. Charset parameter defaults to US_ASCII:
  479. procedure Add_From
  480. (ES : in out Structure;
  481. Address : in String;
  482. Name : in String := "";
  483. Charset : in Character_Set := US_ASCII);
  484. This does not mean that Yolk.Email.Composer.Add_From will encode
  485. Name as US_ASCII, instead it means that the data given in Name
  486. already is US_ASCII. So if Name had contained an ISO-8859-1
  487. encoded String, then the call would've looked like this:
  488. declare
  489. use Yolk.Email;
  490. Email : Structure;
  491. begin
  492. Composer.Add_From
  493. (ES => Email,
  494. Address => "alice@domain.tld",
  495. Name => "Alice",
  496. Charset => ISO_8859_1);
  497. end;
  498. In this case you will end up with a From header looking like
  499. this:
  500. From: =?ISO-8859-1?Q?Alice?= <alice@domain.tld>
  501. So bear in mind that it is your responsibility to ensure that
  502. your data, and the Character_Set parameter match.
  503. The Recipient_Kind type define the kind of recipient that is
  504. being added to an email. If you've worked with email, these three
  505. should be familiar to you:
  506. 1. Bcc
  507. 2. Cc
  508. 3. To
  509. When adding recipients to an email Structure the default is To,
  510. but since not all recipients are equal, you can change the kind
  511. of recipient to Bcc or Cc, according to your needs.
  512. The Structure type is at the core of it all. You declare an
  513. object to be of the Structure type, and then you use the
  514. Yolk.Email.Composer facilities to build and send the email.
  515. 10 Yolk.Email.Composer
  516. The actual tools for building and sending an email is found in
  517. this package. Here are tools for building emails from the ground
  518. up and there are a few convenience procedures if you just need to
  519. send a simple email with no bells or whistles.
  520. I'm not going to go through ever procedure in this package,
  521. instead I'll show an example on how to build an email from the
  522. ground up and how to use one of the convenience procedures.
  523. 10.1 Building and sending an email, the easy way
  524. There are two convenience procedures in Yolk.Email.Composer for
  525. sending emails without having to do a whole lot of work/thinking.
  526. They are both named Send and they look like this:
  527. procedure Send
  528. (ES : in out Structure;
  529. From_Address : in String;
  530. From_Name : in String := "";
  531. To_Address : in String;
  532. To_Name : in String := "";
  533. Subject : in String;
  534. Text_Part : in String;
  535. SMTP_Server : in String := "localhost";
  536. SMTP_Port : in Positive := 25;
  537. Charset : in Character_Set := US_ASCII);
  538. procedure Send
  539. (ES : in out Structure;
  540. From_Address : in String;
  541. From_Name : in String := "";
  542. To_Address : in String;
  543. To_Name : in String := "";
  544. Subject : in String;
  545. Text_Part : in String;
  546. HTML_Part : in String;
  547. SMTP_Server : in String := "localhost";
  548. SMTP_Port : in Positive := 25;
  549. Charset : in Character_Set := US_ASCII);
  550. As you can see, the only difference between these two is that the
  551. first one sends plain text emails, while the second one sends
  552. multipart/alternative with both plain text and HTML parts. Usage
  553. is as simple as:
  554. declare
  555. use Yolk.Email;
  556. Email : Structure;
  557. begin
  558. Composer.Send (ES => Email,
  559. From_Address => "alice@domain.tld",
  560. From_Name => "Alice",
  561. To_Address => "bob@domain.tld",
  562. To_Name => "Bob",
  563. Subject => "Is this thing on?",
  564. Text_Part => "Hey you!",
  565. Charset => ISO_8859_1);
  566. if Composer.Is_Send (Email) then
  567. -- Success!
  568. else
  569. -- Failure!
  570. end if;
  571. end;
  572. It is possible, and allowed, to call some of the various other
  573. procedures prior to calling one of the convenience procedures. If
  574. for example you want to add a custom header, it can be done like
  575. this:
  576. declare
  577. use Yolk.Email;
  578. Email : Structure;
  579. begin
  580. Composer.Add_Custom_Header (ES => Email,
  581. Name => "User-Agent",
  582. Value => "My User Agent");
  583. Composer.Send (ES => Email,
  584. From_Address => "alice@domain.tld",
  585. From_Name => "Alice",
  586. To_Address => "bob@domain.tld",
  587. To_Name => "Bob",
  588. Subject => "Is this thing on?",
  589. Text_Part => "Hey you!",
  590. Charset => ISO_8859_1);
  591. if Composer.Is_Send (Email) then
  592. -- Success!
  593. else
  594. -- Failure!
  595. end if;
  596. end;
  597. And with that, the header User-Agent: is now added to the email:
  598. User-Agent: My User Agent
  599. It hardly gets any easier than that. Lets move on and see how the
  600. above is accomplished the hard way.
  601. 10.2 Building and sending email, the hard way
  602. It is possible to build an email from the ground up, which
  603. obviously allows for a more fine grained control over what is
  604. added. It is also a bit more complicated, but not much. Lets try
  605. and mimick the easy examples, the hard way:
  606. declare
  607. use Yolk.Email;
  608. Email : Structure;
  609. begin
  610. Composer.Add_Custom_Header (ES => Email,
  611. Name => "User-Agent",
  612. Value => "My User Agent");
  613. Composer.Add_From (ES => Email,
  614. Address => "alice@domain.tld",
  615. Name => "Alice",
  616. Charset => ISO_8859_1);
  617. Composer.Add_Recipient (ES => Email,
  618. Address => "bob@domain.tld",
  619. Name => "Bob");
  620. Composer.Set_Subject (ES => Email,
  621. Subject => "Is this thing on?");
  622. Composer.Set_Text_Part (ES => Email,
  623. Part => "Hey you!");
  624. Composer.Add_SMTP_Server (ES => Email,
  625. Host => "localhost");
  626. Composer.Send (ES => Email);
  627. if Composer.Is_Send (Email) then
  628. -- Success!
  629. else
  630. -- Failure!
  631. end if;
  632. end;
  633. Harder yes, but really not all that much more difficult.
  634. 11 Yolk.Handlers
  635. Most web applications will need to handle static content, such as
  636. PNG, HTML and CSS files. Yolk.Handlers helps you accomplish that,
  637. so you don't have to build your own handlers for these kinds of
  638. files.
  639. The following filetypes are supported by Yolk.Handlers:
  640. CSS
  641. GIF
  642. HTML
  643. ICO
  644. JPG
  645. JS
  646. PNG
  647. SVG
  648. XML
  649. XSL
  650. The filetypes that are textual, are compressed according to the
  651. Yolk.Configuration parameter Compress_Static_Content which
  652. defaults to False. The regular expressions for identifying these
  653. filetypes are also defined in Yolk.Configuration by the Handler_*
  654. parameters. These regular expressions are registered by the
  655. AWS.Services.Dispatchers.URI.Register_Regexp procedure.
  656. There's only one procedure in the Yolk.Handlers package:
  657. procedure Set (RH : out AWS.Services.Dispatchers.URI.Handler);
  658. You can see an example on how this is used in the demo file
  659. my_handlers.adb. There's really very little reason not to use
  660. this package for handling of static content, but it is of course
  661. not mandatory.
  662. This package makes use of the Yolk.Static_Content package for the
  663. actual delivery of the content to the user.
  664. 12 Yolk.Log
  665. This package serves two purposes:
  666. 1. It contains the two callback procedures used to write AWS
  667. logging data (access and error) to syslogd.
  668. 2. It creates the SQL, SQL_Cache, SQL_Error, SQL_Select, Alert,
  669. Critical, Debug, Emergency, Error, Info, Notice and Warning
  670. trace handles, and activates them according to the values
  671. defined in the configuration file.
  672. Out of the box, a Yolk application requires a running syslogd
  673. daemon, as all log data is sent to syslogd. If for some reason
  674. you don't want to use syslogd, you're going to have to hack the
  675. Yolk.Log package, or remove it entirely.
  676. The two procedures named AWS_* are used by the AWS HTTP(S)
  677. server. These should not be used for anything else - or rather:
  678. If you use them for anything else, the Message given is going to
  679. be written to syslogd with either the AWS access log label or AWS
  680. error log label. There's absolutely no harm in this, except it
  681. might be a bit confusing when reading the log data.
  682. The Trace procedure is the one that you will be using in your
  683. application. You can send log data to the trace handles defined
  684. in Yolk.Log.Trace_Handles. All log data is then sent to the
  685. syslogd daemon using the facilities set in the configuration file
  686. for the given trace handle.
  687. Using Trace is about as easy as it gets:
  688. Yolk.Log.Trace (Handle => Error,
  689. Message => "Secret sauce to Error!");
  690. If you haven't activated a trace handle, then calling Trace for
  691. that handle does nothing, ie. you don't have to remove all your
  692. Trace calls from the code if you don't use them.
  693. Note that on many systems the Emergency messages are written to
  694. console, so be cautious about using this specific level, unless
  695. you've made certain that you can get to the messages.
  696. 13 Yolk.Not_Found
  697. The job of this package is to return a HTTP 404 status code and
  698. an accompanying simple not found HTML page. It's sole function
  699. Generate is about as simple as they come:
  700. function Generate
  701. (Request : in AWS.Status.Data)
  702. return AWS.Response.Data;
  703. It relies on the template file demo/exe/templates/system/404.tmpl
  704. to generate the generic 404 HTML page, so if you want to use
  705. Yolk.Not_Found in your own application, then remember to bring
  706. along this file. Where the 404.tmpl is placed is defined in the
  707. configuration parameter System_Templates_Path.
  708. Also worth noting is that the Yolk.Not_Found.Generate function is
  709. used as the default callback in the demo application. This means
  710. that all requested resources that doesn't match a registered
  711. dispatcher, is served by Yolk.Not_Found.Generate ie. a 404 is
  712. returned. See the demo/src/my_handlers.adb file for more
  713. information.
  714. 14 Yolk.Process_Control
  715. With Yolk.Process_Control you get the ability to control your
  716. application using the SIGINT, SIGPWR and SIGTERM signals. If you
  717. give your Yolk application the --pid-file commandline argument
  718. when starting it, Yolk.Process_Control will create a PID file on
  719. the given location, if it is allowed to do so. This PID file will
  720. also be deleted when the application terminates. Note that the
  721. extras/rc.yolk script handles all this transparently.
  722. 14.1 Exceptions
  723. These are:
  724. Cannot_Create_PID_File. Is raised if the PID_File cannot be
  725. created, eg. if the application lacks permissions to write to
  726. the directory where the PID_File is located.
  727. Cannot_Delete_PID_File. Is raised if the PID_file cannot be
  728. deleted, eg. if the application lacks permissions to write to
  729. the directory where the PID_File is located, or to the PID_File
  730. itself.
  731. PID_File_Exists. Is raised when the PID_File already exists,
  732. ie. the application is already running or it was shutdown
  733. incorrectly.
  734. 14.2 Using Yolk.Process_Control
  735. When you use the Yolk.Process_Control package the
  736. Unreserve_All_Interrupts pragma is used. This means that
  737. depending on the compiler used one or more interrupt signals may
  738. be affected. In the case of the GNAT compiler, this is
  739. specifically mentioned in the source of the Ada.Interrupts.Names
  740. package:
  741. -- The pragma Unreserve_All_Interrupts affects the following
  742. signal(s):
  743. -- SIGINT: made available for Ada handler
  744. Since neither SIGPWR or SIGTERM are reserved by the compiler, the
  745. Yolk.Process_Control package is able to assume control of these
  746. signals. You can read more about the [http://gcc.gnu.org/onlinedocs/gnat_rm/Pragma-Unreserve_005fAll_005fInterrupts.html||pragma Unreserve_All_Interrupts here]
  747. . If you compile Yolk with a different compiler than GNAT, then
  748. please check if one of the affected signals are reserved.
  749. There are two procedures in the Yolk.Process_Control package:
  750. procedure Stop;
  751. procedure Wait;
  752. When you call the Wait procedure, you basically hang there until
  753. 1. The Stop procedure is called
  754. 2. The application receives a SIGINT, SIGPWR or SIGTERM signal
  755. This is quite handy for applications, that need some sort of loop
  756. to keep them from terminating. You can see an example on how this
  757. can be done in the demo/src/yolk_demo.adb file.
  758. When Wait is called, subsequent calls to Wait are ignored, unless
  759. a call to Stop has been made or the application has received one
  760. of the SIGINT, SIGPWR or SIGTERM signals. So it's perfectly valid
  761. to do:
  762. Wait;
  763. -- Stop called from somewhere in the app
  764. -- Do something...
  765. Wait;
  766. -- The app receives a SIGINT signal
  767. -- Do something...
  768. Wait;
  769. Whether or not this is actually useful I don't know, but it is
  770. possible.
  771. 15 Yolk.Process_Owner
  772. When it is necessary to change the owner of a process, the
  773. Yolk.Process_Owner package is the solution. Obviously this can
  774. also be done when starting the application, using various shell
  775. tricks, but I find it it much cleaner to just let the application
  776. handle it by itself.
  777. 15.1 Exceptions
  778. There's only one:
  779. 1. Username_Does_Not_Exist. This is raised if the given username
  780. doesn't exist on the system.
  781. 15.2 Using Yolk.Process_Owner
  782. There's only a single procedure in this package and its
  783. specification looks like this:
  784. procedure Set_User
  785. (Username : in String);
  786. -- Set the process owner to Username.
  787. Please note that when changing the user ID of the application
  788. with Set_User, the group ID is changed to the first group the
  789. given user is a member of.
  790. Usage is as simple as expected:
  791. declare
  792. begin
  793. Set_User (Username => "billybob");
  794. exception
  795. when Username_Does_Not_Exist =>
  796. -- User is missing. Do something!
  797. end;
  798. In the file demo/src/yolk_demo.adb you'll find that
  799. Yolk.Process_Owner.Set_User is used in conjunction with the
  800. Yolk.Configuration.Yolk_User parameter.
  801. 16 Yolk.Server
  802. This is a convenience package to handle creating, starting and
  803. stopping an AWS HTTP server. The biggest drawback to this package
  804. is that it can only create and manage one AWS server.
  805. Note a potential manual operation when using the Yolk.Server
  806. package : Setting the Cache-Control header. This can become
  807. necessary if you
  808. 1. use Yolk.Server to create and start your AWS server AND
  809. 2. use the Yolk.Handlers package to register dispatchers for
  810. static content AND
  811. 3. have the Compress_Static_Content configuration parameter set
  812. to True AND
  813. 4. aren't content with the default Cache-Control header
  814. In that case you can use the
  815. Yolk.Static_Content.Set_Cache_Options procedure to work your own
  816. magic on the Cache-Control header sent to clients requesting
  817. static content. See the Yolk.Static_Content chapter for more
  818. information.
  819. 16.1 Yolk.Server.Create
  820. Creating a server is done by calling the Create function. This
  821. function accepts one parameter that should be fairly
  822. self-explanatory.
  823. type HTTP is tagged limited private;
  824. function Create
  825. (Unexpected : in AWS.Exceptions.Unexpected_Exception_Handler)
  826. return HTTP;
  827. 16.2 Yolk.Server.Start
  828. procedure Start
  829. (WS : in out HTTP;
  830. Dispatchers : in AWS.Dispatchers.Handler'Class);
  831. This does as expected: Starts the server with Dispatchers.
  832. When calling Start several things happen:
  833. 1. If the configuration parameter Load_MIME_Types_File is True
  834. then the MIME types file given in MIME_Types_File is loaded
  835. into AWS.
  836. 2. If the configuration parameter Start_WebSocket_Servers is True
  837. then the AWS WebSocket servers are started.
  838. 3. Yolk.Static_Content.Static_Content_Cache_Setup is called with
  839. its default parameters if the configuration parameter
  840. Compress_Static_Content is True.
  841. 4. If session support is turned on for the server and the
  842. Session_Data_File exists, then the session data is loaded from
  843. this file.
  844. 5. The Yolk.Log.AWS_Access_Log_Writer procedure is registered to
  845. handle all AWS access log data if the configuration parameter
  846. AWS_Access_Log_Activate is True .
  847. 6. The Yolk.Log.AWS_Error_Log_Writer procedure is registered to
  848. handle all AWS error log data if the configuration parameter
  849. AWS_Error_Log_Activate is True .
  850. 16.3 Yolk.Server.Stop
  851. procedure Stop
  852. (WS : in out HTTP);
  853. This does as expected: Stops everything that was started when the
  854. Yolk.Server.Start call was made. Note that if sessions support is
  855. turned on for the server, then the Dispatchers are switched for a
  856. plain Not Found dispatcher when Stop is called. This is done to
  857. prevent session tampering while the server is shutting down.
  858. 17 Yolk.Static_Content
  859. Most web applications have a lot of static content, ie. stuff
  860. that rarely changes. Things like PNG's, HTML, CSS and Javascript.
  861. These are content types that are common for most websites, so a
  862. application is going to have to handle these in some way. This is
  863. where Yolk.Static_Content comes in. Two kinds of files are
  864. handled by Yolk.Static_Content:
  865. Compressable (XML, HTML, CSS, JS and so on)
  866. Non-compressable (PNG, JPG, GIF, ICO and so on)
  867. It is up to you, the user, to decide whether a specific kind of
  868. file is compressable or not - the package does not make any such
  869. assumptions. The difference between the two, is that compressable
  870. files are compressed prior to delivery, if the clients HTTP
  871. request includes a Accept-Encoding: gzip header. Non-compressable
  872. files are simply returned as is. For both kinds, a generic 404 is
  873. returned if the requested file doesn't exist.
  874. Yolk.Static_Content is affected by five configuration settings:
  875. Compress_Static_Content
  876. Compress_Static_Content_Minimum_File_Size
  877. Compressed_Static_Content_Cache
  878. Compressed_Static_Content_Max_Age
  879. WWW_Root
  880. You should carefully read the demo/exe/configuration/config.ini
  881. file for information on what exactly these do.
  882. 17.1 Yolk.Static_Content.Static_Content_Cache_Setup
  883. The configuration parameter Compressed_Static_Content_Cache
  884. defines where the compressed version of the static content is
  885. saved. When your application is started, this directory may or
  886. may not exist, and it may or may not contain various compressed
  887. files. To make sure that the directory exists and is empty, you
  888. should call the Static_Content_Cache_Setup procedure:
  889. procedure Static_Content_Cache_Setup
  890. (No_Cache : in Boolean := False;
  891. No_Store : in Boolean := False;
  892. No_Transform : in Boolean := False;
  893. Max_Age : in AWS.Messages.Delta_Seconds := 86400;
  894. S_Max_Age : in AWS.Messages.Delta_Seconds :=
  895. AWS.Messages.Unset;
  896. Public : in Boolean := False;
  897. Must_Revalidate : in Boolean := True;
  898. Proxy_Revalidate : in Boolean := False);
  899. This procedure creates the Compressed_Static_Content_Cache
  900. directory if it doesn't already exists and if it exists, it
  901. deletes everything in it. So basically you're left with an empty
  902. directory after a call to this procedure.
  903. The parameters defines the content of the Cache-Control header
  904. sent to the user agent when a request for static content is made.
  905. The default parameters are fairly sane, and will result in a
  906. cache header looking like this:
  907. Cache-Control: max-age=86400, must-revalidate
  908. Read the [http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9||HTTP/1.1 Cache-Control header definition]
  909. for more information on what the various settings do.
  910. If you use Yolk.Server to create and start your AWS server, then
  911. this called is made automatically with it's default parameters.
  912. If you don't use Yolk.Server and you use the Yolk.Static_Content
  913. package to serve static content, then you must call this
  914. procedure before starting the AWS server. Calling it repeatedly
  915. will simply wipe out the compressed cache directory and reset the
  916. Control_Cache header according to the given parameters.
  917. 17.2 Yolk.Static_Content.Compressable
  918. If your application contains a bunch of compressable files of
  919. significant size, you can save a lot of bandwidth by using the
  920. Compressable function to serve them:
  921. function Compressable
  922. (Request : in AWS.Status.Data)
  923. return AWS.Response.Data;
  924. A natural place to call this function would be where you define
  925. your content handlers, as seen in this, slightly altered, excerpt
  926. from the Yolk.Handlers package:
  927. declare
  928. package SC renames Yolk.Static_Content;
  929. Resource_Handlers : AWS.Services.Dispatchers.URI.Handler;
  930. begin
  931. AWS.Services.Dispatchers.URI.Register_Regexp
  932. (Dispatcher => Resource_Handlers,
  933. URI => "\.css$",
  934. Action => Create (Callback =>
  935. SC.Compressable'Access));
  936. AWS.Services.Dispatchers.URI.Register_Regexp
  937. (Dispatcher => Resource_Handlers,
  938. URI => "\.html$",
  939. Action => Create (Callback =>
  940. SC.Compressable'Access));
  941. end;
  942. Here you can see that we've defined some content handlers for CSS
  943. and HTML files based on a few simple regular expressions, and
  944. whenever a resource is requested that matches one of these, the
  945. corresponding Action callback is called, which in this example is
  946. the Compressable function. The following steps are then taken:
  947. 1. Does the requested resource exist and is it an ordinary file?
  948. (a) If no, then return a 404 message
  949. (b) if yes, then proceed to 2
  950. 2. Does the client support compressed content?
  951. (a) If no, then return the requested resource un-compressed
  952. (b) if yes, then proceed to 3
  953. 3. Is there a compressed version of the resource available on
  954. disk?
  955. (a) if no, then make one, cache it for future use, and return
  956. it
  957. (b) If yes, then proceed to 4
  958. 4. Is the age of the compressed file <= Compressed_Max_Age?
  959. (a) If no, then delete the current file, build a new one, cache
  960. it and return it
  961. (b) If yes, then return it
  962. And that's really all there is to it. Note that Compressable
  963. always looks for requested content in the WWW_Root directory, so
  964. if the user requests the file /css/index.css, then the path is
  965. going to be:
  966. WWW_Root & "/css/index.ss";
  967. Using the default configuration value for WWW_Root, we'll end up
  968. with the path:
  969. static_content/css/index.css
  970. The compressed version of index.css is saved as:
  971. Compressed_Static_Content_Cache & "/css/index.css" & ".gz";
  972. Using the default configuration value for
  973. Compressed_Static_Content_Cache, we'll end up with the path:
  974. static_content/compressed_cache/css/index.css.gz
  975. In these two cases the paths are relative to the executable, but
  976. you can of course also define WWW_Root and
  977. Compressed_Static_Content_Cache as absolute paths in the
  978. configuration file.
  979. 17.3 Yolk.Static_Content.Non_Compressable
  980. A lot of static content really doesn't benefit from any further
  981. compression. This is the case for PNG files, JPEG files and a
  982. whole lot of other kinds of files. Static content that has
  983. already been compressed, should be handled by the
  984. Non_Compressable function:
  985. function Non_Compressable
  986. (Request : in AWS.Status.Data)
  987. return AWS.Response.Data;
  988. A call to Non_Compressable is much simpler compared to a call to
  989. it's sibling function Compressable:
  990. 1. Does the requested resource exist and is it an ordinary file?
  991. (a) If no, then return a 404 message
  992. (b) if yes, then return the file
  993. And that's it. Either the resource is there, or it isn't. If the
  994. requested resource is /images/foo.png, then the Non_Compressable
  995. function searches for the resource in:
  996. WWW_Root & "/images/foo.png";
  997. Using the default configuration value for WWW_Root, we'll end up
  998. with the path:
  999. static_content/images/foo.png
  1000. Just as with Compressable a natural place to use Non_Compressable
  1001. is where you define you content handlers, but it can obviously be
  1002. used wherever your application handles requests for static
  1003. content.
  1004. 17.4 Yolk.Static.Set_Cache_Options
  1005. You can alter the Cache-Control header sent to clients requesting
  1006. static content with a call to Set_Cache_Options. This is
  1007. especially handy if you use the Yolk.Server package to create and
  1008. start your server, since the initial call to
  1009. Static_Content_Cache_Setup is hidden you're forced to live with
  1010. the default values given in the Static_Content_Cache_Setup call,
  1011. resulting in a Cache-Control header looking like this:
  1012. Cache-Control: max-age=86400, must-revalidate
  1013. If this is not what you want, you can use Set_Cache_Options to
  1014. change it:
  1015. procedure Set_Cache_Options
  1016. (No_Cache : in Boolean := False;
  1017. No_Store : in Boolean := False;
  1018. No_Transform : in Boolean := False;
  1019. Max_Age : in AWS.Messages.Delta_Seconds := 86400;
  1020. S_Max_Age : in AWS.Messages.Delta_Seconds :=
  1021. AWS.Messages.Unset;
  1022. Public : in Boolean := False;
  1023. Must_Revalidate : in Boolean := True;
  1024. Proxy_Revalidate : in Boolean := False);
  1025. This MUST be called immediately after having called
  1026. Yolk.Server.Start, since the call to Static_Content_Cache_Setup
  1027. in Start will overwrite the cache options.
  1028. 18 Yolk.Syndication
  1029. The Atom Syndication format ([http://tools.ietf.org/html/rfc4287||RFC4287]
  1030. ) is an XML-based document format that describes lists of related
  1031. information known as "feeds". Atom documents are used by many web
  1032. applications as a means of publishing information to users. It's
  1033. not a complicated format, but it does require a bit of work to
  1034. construct a proper Atom feed by hand, and since I try my best to
  1035. avoid work, I made the Yolk.Syndication package. This package
  1036. makes it easy to put together an Atom feed document, and have
  1037. it delivered as a string or an XML/Ada DOM object.
  1038. Yolk.Syndication helps you construct the Atom XML - it does not
  1039. check that the resulting XML is valid Atom XML, ie. that the Atom
  1040. rules have been followed, so if the Atom specification says that
  1041. atom:feed elements MUST contain exactly one atom:id element.
  1042. then it is your job to make sure that your Atom feed has exactly
  1043. one such element. So before venturing into the realm of Atom,
  1044. it's probably a good idea to read [http://tools.ietf.org/html/rfc4287||RFC4287]
  1045. for a good understanding of the requirements for this document
  1046. format. A basic example of how to build an Atom feed can be found
  1047. in the Yolk demo application.
  1048. There are two packages in the Yolk.Syndication hierarchy:
  1049. Yolk.Syndication - Here the necessary types and exceptions are
  1050. defined.
  1051. Yolk.Syndication.Writer - Here the functions and procedures for
  1052. actually creating an Atom feed are defined.
  1053. When time permits, I'll add a Yolk.Syndication.Reader package for
  1054. extraction of data from an Atom feed.
  1055. 18.1 Exceptions
  1056. There's one exception in this package:
  1057. Not_Valid_XML. Is raised when some Xhtml content is not valid
  1058. XML. This exception can be raised by all procedures that takes
  1059. Xhtml as content.
  1060. 18.2 The Yolk.Syndication types
  1061. There are 5 important types in this package:
  1062. Text_Kinds
  1063. Relation_Kinds
  1064. Atom_Entry
  1065. Atom_Entry_Source
  1066. Atom_Feed
  1067. A few of the procedures in Yolk.Syndication.Writer has parameters
  1068. of the Text_Kinds type. This identifies the kind of data that is
  1069. being added, with possible values being
  1070. Text
  1071. Html
  1072. Xhtml
  1073. Hopefully it's obvious what each of these refers to. Procedures
  1074. that have parameters of the Text_Kinds type always add data to
  1075. the feed that can be interpreted as one of these three kinds of
  1076. text.
  1077. The Relation_Kinds type is used in correlation with links that
  1078. are added to the feed. It identifies how the link relates to the
  1079. current feed/entry. There are 5 possible relations:
  1080. Alternate: Signifies that the link points to an alternate
  1081. version of the resource described by the containing element.
  1082. Related: Signifies that the link points to a resource that is
  1083. related to, but not the same as, the resource described by the
  1084. containing element.
  1085. Self: Signifies that the link points to a resource that is
  1086. equivalent to the resource described by the containing element.
  1087. All feeds should have one link with a Self relation pointing to
  1088. itself.
  1089. Enclosure: Signifies that the link points to a resource that is
  1090. related to, but not the same as, the resource described by the
  1091. containing element. It also signifies that the resource
  1092. potentially is large in size and might require special
  1093. handling. If Enclosure is used, then usually the Length
  1094. parameter is set to hint at the size of the resource.
  1095. Via: Signifies that the resource provided by the containing
  1096. element originates in the URI given by the Href parameter of
  1097. the link element.
  1098. Finally we have the most important types:
  1099. Atom_Entry: An entry in a feed.
  1100. Atom_Entry_Source: The origin of an entry.
  1101. Atom_Feed: The Atom feed.
  1102. These are core to the functionality of Yolk.Syndication, and
  1103. every single procedure and function in the
  1104. Yolk.Syndication.Writer package use one or the other. These three
  1105. types are private, so the only way to declare an object of either
  1106. one, is by calling the New_Atom_Feed, New_Atom_Entry or
  1107. New_Atom_Entry_Source functions respectively.
  1108. Note that only Atom_Feed is thread safe. The two entry related
  1109. types are not.
  1110. 18.3 Yolk.Syndication.New_Atom_Feed
  1111. This function initialize an Atom_Feed type, and its specification
  1112. looks like this:
  1113. function New_Atom_Feed
  1114. (Base_URI : in String := None;
  1115. Language : in String := None;
  1116. Max_Age : in Duration := 5_616_000.0;
  1117. Max_Entries : in Positive := 100;
  1118. Min_Entries : in Positive := 10)
  1119. return Atom_Feed;
  1120. The Base_URI parameter establish a base for resolving relative
  1121. references in the feed. The Language parameter indicates the
  1122. natural language used in the feed. Max_Age is a duration that
  1123. determine when an entry in the feed is old enough to be deleted.
  1124. Max_Entries is the amount of entries kept in the feed. If there
  1125. are more than this amount of entries in the feed, then the oldest
  1126. are deleted until the feed contains Max_Entries entries again.
  1127. Min_Entries is the minimum amount of entries that must be present
  1128. in the feed, before we bother deleting entries whose age is >
  1129. Max_Age. If there are less than Min_Entries entries in the feed,
  1130. then we keep even a 100 year old entry.
  1131. What these parameters hints at, is that some automatic
  1132. maintenance is done when using Yolk.Syndication, and this is
  1133. indeed true. Usually you do not want a feed to grow forever, and
  1134. instead of having to manually clear away old stuff,
  1135. Yolk.Syndication handles all this for you, according to the
  1136. values given when you instantiate an Atom_Feed object using
  1137. New_Atom_Feed.
  1138. 18.4 Yolk.Syndication.New_Atom_Entry
  1139. This function initialize an Atom_Entry type, and its
  1140. specification looks like this:
  1141. function New_Atom_Entry
  1142. (Base_URI : in String := None;
  1143. Language : in String := None)
  1144. return Atom_Entry;
  1145. As you can see, it's a lot simpler than the New_Atom_Feed
  1146. function. This is of course because all the automatic maintenance
  1147. related parameters have already been set by they New_Atom_Feed
  1148. function. All that is left is to define the Base_URI and the
  1149. natural Language used by the entry.
  1150. 18.5 Yolk.Syndication.New_Atom_Entry_Source
  1151. This function initialize an Atom_Entry_Source type, and its
  1152. specification looks like this:
  1153. function New_Atom_Entry_Source
  1154. (Base_URI : in String := None;
  1155. Language : in String := None)
  1156. return Atom_Entry_Source;
  1157. As you can see, this is very similar to the New_Atom_Entry
  1158. function, since its job is to return an Atom_Entry_Source object,
  1159. which is basically just an object that describes the origins of a
  1160. feed entry.
  1161. 18.6 Yolk.Syndication.Writer
  1162. In this package we find all the tools necessary to build the Atom
  1163. XML. There are far too many subprograms in
  1164. Yolk.Syndication.Writer to list here, so instead I'll refer you
  1165. to the source code. All the subprograms in this package work on
  1166. one of the Atom_Feed, Atom_Entry or Atom_Entry_Source types, for
  1167. example if you want to set a title on your feed, you'll use the
  1168. Set_Title procedure:
  1169. procedure Set_Title
  1170. (Feed : in out Atom_Feed;
  1171. Title : in String;
  1172. Base_URI : in String := None;
  1173. Language : in String := None;
  1174. Title_Kind : in Text_Kinds := Text);
  1175. Or if the title is for an entry, then:
  1176. procedure Set_Title
  1177. (Entr : in out Atom_Entry;
  1178. Title : in String;
  1179. Base_URI : in String := None;
  1180. Language : in String := None;
  1181. Title_Kind : in Text_Kinds := Text);
  1182. Or we could add an author to the feed/entry/entry source:
  1183. procedure Add_Author
  1184. (Feed : in out Atom_Feed;
  1185. Name : in String;
  1186. Base_URI : in String := None;
  1187. Email : in String := None;
  1188. Language : in String := None;
  1189. URI : in String := None);
  1190. procedure Add_Author
  1191. (Entr : in out Atom_Entry;
  1192. Name : in String;
  1193. Base_URI : in String := None;
  1194. Email : in String := None;
  1195. Language : in String := None;
  1196. URI : in String := None);
  1197. procedure Add_Author
  1198. (Entry_Source : in out Atom_Entry_Source;
  1199. Name : in String;
  1200. Base_URI : in String := None;
  1201. Email : in String := None;
  1202. Language : in String := None;
  1203. URI : in String := None);
  1204. There's actually a hint about the kind of XML element that is
  1205. being produced by these procedures. If the name of the procedure
  1206. starts with Set_ then it hints at an XML element of which there's
  1207. only ever one. So Set_Title creates a <title>Foo</title> element,
  1208. and if called again, overwrites the previous value, whereas if
  1209. the name of procedure starts with Add_ then the Atom
  1210. specification allows for multiples of these, as can be seen with
  1211. the Add_Author procedure. Calling this one creates an <author>
  1212. element, and calling it again simply adds one more author to the
  1213. feed/entry/entry source.
  1214. Most of the subprograms in this package deals directly with
  1215. building the Atom XML, but there are a few exceptions, as you
  1216. will see next.
  1217. 18.7 Counting the amount of entries in a feed
  1218. If you need to know how many entries that are currently in a
  1219. feed, the you can use the
  1220. Yolk.Syndication.Writer.Amount_Of_Entries function:
  1221. function Amount_Of_Entries
  1222. (Feed : in Atom_Feed)
  1223. return Natural;
  1224. 18.8 Clearing and deleting entries
  1225. There are two procedures available for clearing and deleting
  1226. entries, one for clearing all entries away, and one for deleting
  1227. entries based on their Id. Lets start with the one that clears
  1228. out everything:
  1229. procedure Clear_Entry_List
  1230. (Feed : in out Atom_Feed);
  1231. Calling Clear_Entry_List deletes every single entry that has been
  1232. added to the Feed object so far. A less destructive procedure is
  1233. Delete_Entry:
  1234. procedure Delete_Entry
  1235. (Feed : in out Atom_Feed;
  1236. Id : in String);
  1237. Using this one, you can delete all entries whose Id is Id. Note
  1238. that the match must be exact, so case matters. FOO is not the
  1239. same as foo.
  1240. 18.9 Getting the Atom feed
  1241. When you've added titles, authors, categories, entries and
  1242. content to your feed, the next step is turning it into XML. This
  1243. can be done in one of two ways:
  1244. 1. As string XML.
  1245. 2. As an XML/Ada DOM XML object.
  1246. The string XML is obviously for the final audience, whereas the
  1247. DOM XML is useful if you need to do some further work on the feed
  1248. before releasing it on the world. Here they are:
  1249. function Get_XML_DOM
  1250. (Feed : in Atom_Feed)
  1251. return DOM.Core.Document;
  1252. function Get_XML_String
  1253. (Feed : in Atom_Feed;
  1254. Pretty_Print : in Boolean := False)
  1255. return String;
  1256. Note that if Pretty_Print is True then whitespace is going get
  1257. mangled according to these rules:
  1258. If Pretty_Print is true, then the XML nodes will be indented so
  1259. that children nodes are to the right of their parents. It is set
  1260. to False by default because its use changes the document
  1261. (addition or removal of whitespaces among other things), which in
  1262. general has no effect for automatic tools reading the document.
  1263. All whitespaces are modified outside of elements containing
  1264. nothing but text nodes. For text nodes, leading and trailing
  1265. whitespaces are also deleted.
  1266. Get_XML_String relies on the Write function from XML/Ada to
  1267. generate its output, and the above quote is taken straight from
  1268. the XML/Ada source comments. It's also worth noting that
  1269. Get_XML_String and Get_XML_DOM both are pretty resource-hungry,
  1270. so it's probably best to cache the results for later use, instead
  1271. of calling them on each and every hit.
  1272. 19 Yolk.Utilities
  1273. This package contains various support functionality.
  1274. 20 Yolk.Whoops
  1275. This package contains one single procedure:
  1276. Unexpected_Exception_Handler. I suspect the name gives away
  1277. exactly what this procedure does. It looks like this:
  1278. procedure Unexpected_Exception_Handler
  1279. (E : Ada.Exceptions.Exception_Occurrence;
  1280. Log : in out AWS.Log.Object;
  1281. Error : AWS.Exceptions.Data;
  1282. Answer : in out AWS.Response.Data);
  1283. You can use this procedure to catch any and all exceptions you've
  1284. failed to catch in your application, ie. the ones AWS pickup and
  1285. fail to do anything sensible with. When
  1286. Unexpected_Reception_Handler catch an exception, two things
  1287. happen:
  1288. 1. The exception is logged to the Yolk.Log.Error trace.
  1289. 2. A HTTP status code 500 is returned to the user.
  1290. The template used to create the HTTP code 500 error message is
  1291. demo/exe/templates/system/500.tmpl. You can of course change this
  1292. to match the look and feel of your application. The path to the
  1293. 500.tmpl file is set by the configuration setting
  1294. System_Templates_Path.
  1295. You can see an example on how to use this procedure in the file
  1296. demo/src/yolk_demo.adb.