PageRenderTime 47ms CodeModel.GetById 12ms app.highlight 13ms RepoModel.GetById 1ms 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

Large files files are truncated, but you can click here to view the full file

   1Yolk Manual
   2
   3Revised December 11th. 2012
   4
   5
   6
   7
   8Table of Contents
   9
  10
  11
  12
  13
  14
  15General Information
  16
  171 Copyright and License
  18
  19This document is copyright (C) 2010-, Thomas Løcke. You may copy 
  20this document, in whole or in part, in any form or by any means, 
  21as is or with alterations, provided that (1) alterations are 
  22clearly marked as alterations and (2) this copyright notice is 
  23included unmodified in any copy.
  24
  25Yolk is GPLv3 software. You should have received a copy of the 
  26GNU General Public License and a copy of the GCC Runtime Library 
  27Exception along with this program; see the files COPYING3 and 
  28COPYING.RUNTIME respectively. If not, see [http://www.gnu.org/licenses/||http://www.gnu.org/licenses/]
  29. 
  30
  312 What is Yolk?
  32
  33Yolk is a collection of packages that aim to help build solid 
  34web-applications using Ada. Yolk itself doesn't do a whole lot 
  35that can't be accomplished simply by using [http://libre.adacore.com/libre/tools/aws/||AWS]
  36 and the [http://libre.adacore.com/libre/tools/gnat-component-collection/||GNAT Component Collection (GNATcoll)]
  37, but it does make the job of building complete web-applications 
  38a bit simpler. Things like changing user for the running 
  39application, catching POSIX signals such as SIGKILL, sending log 
  40data to syslogd, adding basic static content handlers, creating 
  41and starting/stopping an AWS powered HTTP server and building 
  42Atom syndication XML are all made a bit easier with Yolk.
  43
  44A Yolk application is in reality an AWS application, with some 
  45sugar added, so you're not really building a Yolk 
  46web-application, as much as you're building an AWS 
  47web-application. What I'm getting at, is that you need to 
  48understand how to use AWS, in order for Yolk to make any kind of 
  49sense. What you get when using Yolk is the little things that AWS 
  50does not readily provide.
  51
  522.1 The Yolk demo application
  53
  54Reading this manual will of course (I hope!) help you understand 
  55how to use Yolk, but please consider taking a closer look at the 
  56Yolk demo application to get a feel for how Yolk is actually 
  57used. The demo is heavily commented, so it should be fairly easy 
  58to understand what's going on. The demo application is also very 
  59suitable as a foundation for other AWS/Yolk applications.
  60
  61It is much easier to show how to use Yolk, than it is to write 
  62down all possible usage scenarios. With the combination of this 
  63manual, the Yolk source files and the demo application, you 
  64should be able to make full use of the Yolk packages in your own 
  65applications.
  66
  672.2 The source code
  68
  69The Yolk source code is the best documentation there is. This 
  70document is never going to be as comprehensive as the actual 
  71source, so I'll strongly suggest having the source code available 
  72as you read this document. What you will find in this document 
  73are short descriptions of what a package is meant to do and 
  74perhaps small usage examples, not a complete rundown of every 
  75type and procedure in a package.
  76
  772.3 Building and installing Yolk
  78
  79See the README and INSTALL files. These are found in the Yolk 
  80root directory.
  81
  822.4 The files Yolk depend upon
  83
  84When you read this document and the Yolk source code, you'll 
  85notice that quite a few packages depend on various files being 
  86available at specified locations. This is for example the case 
  87with the Yolk.Whoops package that expects its template file to be 
  88found at the path templates/system/500.tmpl
  89
  90All such “dependencies” will of course be noted accordingly as we 
  91go along, but instead of forgetting one or more in your own 
  92application, I'd much rather encourage using the demo application 
  93as a foundation for your own applications, since all these fixed 
  94paths and files has been properly added to the demo.
  95
  96I also recommend compiling and running the demo, to make sure 
  97your Yolk install is working as intended. Just read the 
  98demo/README and demo/INSTALL files for instructions on how to get 
  99it up and running.
 100
 1012.5 The Yolk packages naming
 102
 103The Yolk packages are pretty diverse, ranging from process 
 104control to sending email. I've tried naming them as sensibly as 
 105possible, in the hope that the package names alone give away 
 106their function. If I've failed, well, you're just going to have 
 107to refer to this document or take a look at the source for 
 108yourself.
 109
 110
 111
 112
 113The Yolk Packages
 114
 1153 Yolk
 116
 117The Yolk main package currently only contain only a few things: 
 118The Yolk Version string, a Config_File function to get the 
 119location of the configuration file and a PID_File function to get 
 120the location of the PID file. These are used in a few places, for 
 121example in the directory.tmpl template file (the version string), 
 122in the Yolk.Configuration package (the Config_File function) and 
 123in the Yolk.Process_Control package (the PID_File function).
 124
 125All Yolk applications accepts two commandline arguments:
 126
 127• --yolk-config-file : Defines the location of the configuration 
 128  file. If empty or not set, then use the default location 
 129  configuration/config.ini.
 130
 131• --pid-file : Defines the location of the PID file. If empty or 
 132  not set, then don't write a PID file. Note that if you use the 
 133  extras/rc.yolk script to control your application, then this is 
 134  handled for you transparently.
 135
 1364 Yolk.Cache.Discrete_Keys
 137
 138If a piece of data doesn't change very often and it is expensive 
 139to build, then caching it might be worthwhile. Instead of going 
 140to a file or database on every hit, you simply go to the cache 
 141and grab the latest version from there. This is very fast, at the 
 142cost of some memory.
 143
 144If you know exactly what you want to cache, the 
 145Yolk.Cache.Discrete_Keys package might be just what you need.
 146
 1474.1 The generic formal parameters
 148
 149These are:
 150
 151generic
 152
 153   type Key_Type is (<>);
 154
 155   type Element_Type is private;
 156
 157   Max_Element_Age : Duration := 3600.0;
 158
 159package Yolk.Cache.Discrete_Keys is
 160
 161...
 162
 163
 164The Max_Element_Age defaults to one hour. You should obviously 
 165set this to whatever suits your needs. This timer is used for all 
 166content in the cache. You cannot set this individually for each 
 167element.
 168
 1694.2 Instantiation
 170
 171If for example we have two different sets of data (Foo and Bar) 
 172that are expensive to build, we can instantiate a Discrete_Keys 
 173package to handle this:
 174
 175type Cache_Keys is (Foo, Bar);
 176
 177package My_Cache is new Yolk.Cache.Discrete_Keys
 178
 179  (Key_Type     => Cache_Keys,
 180
 181   Element_Type => Unbounded_String);
 182
 183
 184And that is all. We now have a My_Cache object that can hold two 
 185objects: Foo and Bar. These are of the type Unbounded_String and 
 186they have a Max_Element_Age of 3600.0 seconds.
 187
 1884.3 Writing to the cache
 189
 190Before we can read something from the cache, we must first write 
 191something to it:
 192
 193declare
 194
 195   Foo_Value : Unbounded_String := To_Unbounded_String ("Foo");
 196
 197begin
 198
 199   My_Cache.Write (Key   => Foo,
 200
 201                   Value => Foo_Value);
 202
 203end;
 204
 205
 206That is all it takes: “Foo” is now safely tucked away in the 
 207My_Cache object, and will be so for 3600.0 seconds. Calling Write 
 208with the Foo key will always overwrite earlier written Foo 
 209elements, no matter their age.
 210
 2114.4 Reading from the cache
 212
 213A cache obviously only makes sense if you intend to read from it. 
 214In our case we want to get our hands on the previously written “
 215Foo” value:
 216
 217declare
 218
 219   Valid : Boolean := False;
 220
 221   Value : Unbounded_String;
 222
 223begin
 224
 225   My_Cache.Read (Key      => Foo,
 226
 227                  Is_Valid => Valid,
 228
 229                  Value    => Value);
 230
 231   if Valid then
 232
 233      --  do something interesting with the data
 234
 235   else
 236
 237      --  the Foo data is invalid.
 238
 239   end if;
 240
 241end;
 242
 243
 244In order for an element to be valid (the Is_Valid parameter is 
 245true), it must:
 246
 2471. have been added to the cache in the first place
 248
 2492. be younger than Max_Element_Age
 250
 251If Is_Valid is False, then Value is undefined. Note that if 
 252Is_Valid is False then Key is removed from the cache, if it 
 253exists.
 254
 2554.5 Checking if a key is valid
 256
 257If you need to check whether a specific key exists in the cache 
 258and is valid, then you must use the Is_Valid function.
 259
 260if My_Cache.Is_Valid (Foo) then
 261
 262   --  Foo is good!
 263
 264else
 265
 266   --  Foo is bad!
 267
 268end if;
 269
 270
 271This follows the same rules as the Is_Valid parameter for the 
 272Read procedure.
 273
 2744.6 Clearing keys and the entire cache
 275
 276For clearing of keys and the entire cache we have, naturally, two 
 277Clear procedures:
 278
 279-- First we clear the Foo key
 280
 281My_Cache.Clear (Key => Foo);
 282
 283
 284
 285--  And then we clear the entire cache
 286
 287My_Cache.Clear;
 288
 289
 290And that's all it takes.
 291
 2924.7 Cleanup - Getting rid of stale elements
 293
 294Calling Cleanup will delete all stale elements from the cache:
 295
 296My_Cache.Cleanup;
 297
 298
 299Note that this is potentially a very expensive operation if the 
 300cache is large, as the entire cache is iterated and every element 
 301tested for its age. Use with care.
 302
 3035 Yolk.Cache.String_Keys
 304
 305This package is almost similar to the Yolk.Cache.Discrete_Keys 
 306package. The biggest difference is that where the Discrete_Keys 
 307cache package requires that you define a type for the keys, this 
 308package use regular String as keys.
 309
 310The implications of this difference between the two cache 
 311packages are subtle. Both have the same Read, Write, Is_Valid and 
 312Clear procedures and functions, so in that sense the two packages 
 313are the same. The biggest difference lies in the available 
 314generic formal parameters and the functionality of the Cleanup 
 315procedure.
 316
 3175.1 The generic formal parameters
 318
 319These are:
 320
 321generic
 322
 323   type Element_Type is private;
 324
 325   Cleanup_Size      : Positive := 200;
 326
 327   Cleanup_On_Write  : Boolean  := True;
 328
 329   Max_Element_Age   : Duration := 3600.0;
 330
 331   Reserved_Capacity : Positive := 100;
 332
 333package Yolk.Cache.Discrete_Keys is
 334
 335...
 336
 337
 338When the amount of elements in the cache >= Cleanup_Size, then 
 339the Cleanup procedure is called by Write, if Cleanup_On_Write is 
 340set to Boolean True. Cleanup_Size is a sort of failsafe for this 
 341cache package. Since we can't know for sure what is being added 
 342(we don't know the keys beforehand), we need to make sure it 
 343doesn't gobble up all available resources. Set this number high 
 344enough that it'll never tricker under normal circumstances, but 
 345low enough that it'll prevent resource exhaustion in case of 
 346errors.
 347
 348The Max_Element_Age defaults to one hour. You should obviously 
 349set this to whatever suits your needs. This timer is used for all 
 350content in the cache. You cannot set this individually for each 
 351element.
 352
 353Reserved_Capacity should be set as close as possible to the 
 354expected final size of the cache. If your best guestimate is 200 
 355elements in the cache, then set this to 200. Note that this 
 356setting has no bearing on the actual size of the cache. The cache 
 357will happily grow beyond the Reserved_Capacity value.
 358
 3595.2 Instantiation
 360
 361Instantiating String_Keys is done like this:
 362
 363package My_Cache is new Yolk.Cache.String_Keys
 364
 365  (Element_Type      => Unbounded_String,
 366
 367   Reserved_Capacity => 200);
 368
 369
 370And that is all. We now have a My_Cache object that can hold 
 371objects of the type Unbounded_String, all of which have a 
 372Max_Element_Age of 3600.0 seconds. Also we've told the cache to 
 373set aside at least 200 positions for content.
 374
 3755.3 Writing to the cache
 376
 377Before we can read something from the cache, we must first write 
 378something to it:
 379
 380declare
 381
 382   Value : Unbounded_String := To_Unbounded_String ("42");
 383
 384begin
 385
 386   My_Cache.Write (Key   => "Foo",
 387
 388                   Value => Value);
 389
 390end;
 391
 392
 393“42” is now safely tucked away in the My_Cache object under the 
 394key “Foo”, and will be so for 3600.0 seconds. Calling Write with 
 395the “Foo” String will always overwrite earlier written “Foo” 
 396elements, no matter their age.
 397
 3985.4 Reading from the cache
 399
 400A cache obviously only makes sense if you intend to read from it. 
 401In our case we want to get our hands on the previously written “
 402Foo” value:
 403
 404declare
 405
 406   Valid : Boolean := False;
 407
 408   Value : Unbounded_String;
 409
 410begin
 411
 412   My_Cache.Read (Key      => "Foo",
 413
 414                  Is_Valid => Valid,
 415
 416                  Value    => Value);
 417
 418   if Valid then
 419
 420      --  do something interesting with the data
 421
 422   else
 423
 424      --  the Foo data is invalid.
 425
 426   end if;
 427
 428end;
 429
 430
 431In order for an element to be valid (the Is_Valid parameter is 
 432true), it must:
 433
 4341. have been added to the cache in the first place
 435
 4362. be younger than Max_Element_Age
 437
 438If Is_Valid is False, then Value contains undefined garbage. Note 
 439that if Is_Valid is False then Key is removed from the cache, if 
 440it exists.
 441
 4425.5 Checking if a key is valid
 443
 444If you need to check whether a specific key exists in the cache 
 445and is valid, then you need to use the Is_Valid function.
 446
 447if My_Cache.Is_Valid ("Foo") then
 448
 449   --  Foo is good!
 450
 451else
 452
 453   --  Foo is bad!
 454
 455end if;
 456
 457
 458This follows the same rules as the Is_Valid parameter for the 
 459Read procedure.
 460
 4615.6 Clearing keys and the entire cache
 462
 463For clearing of keys and the entire cache we have, naturally, two 
 464Clear procedures:
 465
 466-- First we clear the Foo key
 467
 468My_Cache.Clear (Key => "Foo");
 469
 470
 471
 472--  And then we clear the entire cache
 473
 474My_Cache.Clear;
 475
 476
 4775.7 How much is in there?
 478
 479With the Discrete_Keys cache we obviously always know the exact 
 480amount of keys available, since we've defined the keys ourselves. 
 481This is not the case with the String_Keys cache, where any String 
 482can be a key. If we need to know how many elements that are 
 483currently in the cache, we call the Length function:
 484
 485if My_Cache.Length > 1000 then
 486
 487   --  Woa! Lots of stuff in the cache..
 488
 489end if;
 490
 491
 492Note that Length count both valid and invalid elements.
 493
 4945.8 Cleanup - Keeping cache size in check
 495
 496if Cleanup_On_Write is True, then Cleanup is called by Write 
 497whenever the size of the cache reach Cleanup_Size. It is of 
 498course also possible to call it manually:
 499
 500if My_Cache.Length > 1000 then
 501
 502   My_Cache.Cleanup;
 503
 504end if;
 505
 506
 507If you've set Cleanup_On_Write to Boolean False and the String 
 508keys are coming from outside sources, then you really should make 
 509sure you call Cleanup on a regular basis.
 510
 5116 Yolk.Command_Line
 512
 513This package enables you to fetch the value of a given 
 514commandline parameter using the Get function:
 515
 516function Get       
 517
 518  (Parameter : in String;       
 519
 520   Default   : in String := "")        
 521
 522   return String;
 523
 524
 525If Parameter is found Get will return the value immediately 
 526following Parameter. If Parameter isn't found Default is 
 527returned. 
 528
 5297 Yolk.Config_File_Parser
 530
 531This package enables you to access KEY/VALUE pairs in 
 532configuration files that are written in the style:
 533
 534# This is a comment
 535
 536-- This is also a comment
 537
 538KEY VALUE
 539
 540Keys are case-insensitive, so FOO, foo and fOo are all the same. 
 541Blank lines and comments are ignored and so is pre/postfixed 
 542whitespace. It is not necessary to quote values that contain 
 543whitespace, to this:
 544
 545KEY some value with whitespace
 546
 547is perfectly valid, and will return “some value with whitespace” 
 548when calling Get (KEY). If VALUE is Boolean True or False 
 549(case-insensitive), then the KEY can be returned as a String or a 
 550Boolean, depending on the target type. If the target type does 
 551not match the VALUE and no sensible conversion can be made, then 
 552a Conversion_Error exception is raised. No dummy values are 
 553returned at any time.
 554
 555To clear a default value, simply add the key to the configuration 
 556file, with no value set.
 557
 5587.1 The generic formal parameters
 559
 560These are:
 561
 562generic
 563
 564   use Ada.Strings.Unbounded;
 565
 566   type Key_Type is (<>);
 567
 568   type Defaults_Array_Type is array (Key_Type) of 
 569Unbounded_String;
 570
 571   Defaults    : in Defaults_Array_Type;    
 572
 573   Config_File : in String;
 574
 575package Yolk.Config_File_Parser is
 576
 577...
 578
 579
 580Config_File is of course the name and location of the 
 581configuration file.
 582
 5837.2 Exceptions
 584
 585There are 3 different exceptions that can be raised by the 
 586Yolk.Config_File_Parser package. These are:
 587
 588• Unknown_Key. This is raised if an unknown key has been found in 
 589  the configuration file given when instantiating the package or 
 590  when Load_File is called.
 591
 592• Cannot_Open_Config_File. This is raised when Config_File cannot 
 593  be read.
 594
 595• Conversion_Error. This is raised when a value cannot be 
 596  converted to the target type, ie. the value “42” to a Boolean.
 597
 5987.3 Instantiation
 599
 600Yolk.Config_File_Parser is a generic package, so in order to use 
 601it, you have to instantiate it, like this:
 602
 603package My_Configuration is
 604
 605
 606
 607   type Keys is (Foo, Bar);
 608
 609   type Defaults_Array is array (Keys) of Unbounded_String;
 610
 611
 612
 613   Default_Values : constant Defaults_Array :=
 614
 615                      (Foo => To_Unbounded_String ("some foo"),
 616
 617                       Bar => To_Unbounded_String ("some bar"));
 618
 619   
 620
 621   package Config is new Yolk.Config_File_Parser
 622
 623     (Key_Type => Keys,
 624
 625      Defaults_Array_Type => Defaults_Array,
 626
 627      Defaults => Default_Value,
 628
 629      Config_File => "config.ini");
 630
 631
 632
 633end My_Configuration;
 634
 635
 636Here we instantiate the Config package with config.ini as the 
 637configuration file. This means that KEY/VALUE pairs found in this 
 638file will overwrite the default values set in the Default_Values 
 639array. Setting a default value to Null_Unbounded_String means the 
 640value is empty.
 641
 642Note that the config.ini file does not have to contain all the 
 643valid keys. It is perfectly fine to only add those keys that have 
 644non-default values to the configuration file.
 645
 6467.4 Re-loading configuration files
 647
 648With the Load_File procedure you can re-load a new configuration 
 649file into your Config package:
 650
 651My_Configuration.Config.Load_File ("new_config.ini");
 652
 653
 654Now the KEY/VALUE pairs of new_config.ini will overwrite the ones 
 655originally found in the config.ini file the package was 
 656instantiated with. You can do this as many times as you like. 
 657Note that you cannot change what KEY's are valid, so if the 
 658new_config.ini file contains unknown keys, Load_File will raise 
 659the Unknown_Key exception.
 660
 6617.5 Getting values
 662
 663With instantiation and loading of configuration files out of the 
 664way, it is now time to get to the configuration values. To get 
 665the value of the Foo key, you do:
 666
 667My_Configuration.Config.Get (Foo);
 668
 669
 670There are Get functions for the following types:
 671
 672• Boolean
 673
 674• Duration
 675
 676• Float
 677
 678• Integer
 679
 680• String
 681
 682• Unbounded_String
 683
 684Empty keys simply return an empty String or a 
 685Null_Unbounded_String, depending on the target type. If a key is 
 686empty and the target type is not a String or an Unbounded_String, 
 687then the Conversion_Error exception is raised.
 688
 6897.6 Checking if a KEY has a VALUE
 690
 691You can check if a key has a value with the Has_Value function:
 692
 693if Has_Value (Foo) then
 694
 695   Put_Line ("Foo has a value");
 696
 697end if;
 698
 699
 700Basically all this function does is return Boolean True if the 
 701value of the given key is not a Null_Unbounded_String.
 702
 7038 Yolk.Configuration
 704
 705This package is a bit of an oddball, as all it does is 
 706instantiate the Yolk.Config_File_Parser generic with the default 
 707AWS and Yolk configuration values. This is used by Yolk 
 708internally, but also by the AWS component of your application. 
 709The instantiation looks like this:
 710
 711package Config is new Config_File_Parser
 712
 713  (Key_Type            => Keys,
 714
 715   Defaults_Array_Type => Defaults_Array,
 716
 717   Defaults            => Default_Values,
 718
 719   Config_File         => Config_File);
 720
 721
 722The Config_File function call return either the default Yolk 
 723configuration file (configuration/config.ini) or a user specified 
 724configuration file given by the --yolk-config-file command line 
 725argument, so starting for example the Yolk demo like this:
 726
 727./yolk_demo --yolk-config-file /etc/yolk-config.ini
 728
 729will force the demo to look for the /etc/yolk-config.ini 
 730configuration file.
 731
 732There's a fully commented config.ini.dist file available in the 
 733extras/ directory. I recommend taking a look at the Yolk demo 
 734application to see how the Yolk.Configuration package is used.
 735
 7368.1 Get the AWS specific configuration settings
 737
 738On some occassions it might be necessary to get the AWS 
 739configuration object. This can easily be accomplished by calling 
 740the Get_AWS_Configuration function:
 741
 742AWS_Config : constant AWS.Config.Object := 
 743
 744  Yolk.Configuration.Get_AWS_Configuration;
 745
 746
 7479 Yolk.Email
 748
 749Using Yolk.Email and the child package Yolk.Email.Composer you 
 750can build and send more or less any kind of email:
 751
 752• Plain text
 753
 754• Multipart/Alternative
 755
 756• Multipart/Mixed
 757
 758The package supports adding multiple SMTP servers, meaning you 
 759can add as many as you need, and the email will then be send via 
 760the first one that accepts it.
 761
 762The Yolk.Email package define 4 exceptions and 3 types. The 
 763facilities for actually constructing and sending the email are 
 764found in Yolk.Email.Composer.
 765
 7669.1 Exceptions
 767
 768These are:
 769
 770• Attachment_File_Not_Found. Is raised if a file attachment is 
 771  not found at the given path.
 772
 773• No_Address_Set. Is raised if the address component of a To, 
 774  Reply-To, From, Bcc/Cc header is missing.
 775
 776• No_Sender_Set_With_Multiple_From. Is raised when an email 
 777  contains multiple From headers but no Sender header, as per 
 778  RFC-5322, 3.6.2. http://tools.ietf.org/html/rfc5322
 779
 780• No_SMTP_Host_Set. Is raised if the SMTP host list is empty, ie. 
 781  no SMTP host has been set for sending the email.
 782
 7839.2 The Yolk.Email types
 784
 785When using Yolk.Email.Composer to build and send emails, three 
 786types declared in Yolk.Email are central:
 787
 7881. Character_Set
 789
 7902. Recipient_Kind
 791
 7923. Structure
 793
 794The Character_Set type define what character set is used when 
 795data is added to an email Structure object. For example looking 
 796at the Yolk.Email.Composer.Add_From procedure, we see that the 
 797Charset parameter defaults to US_ASCII:
 798
 799procedure Add_From      
 800
 801  (ES        : in out Structure;       
 802
 803   Address   : in     String;       
 804
 805   Name      : in     String := "";       
 806
 807   Charset   : in     Character_Set := US_ASCII);
 808
 809
 810This does not mean that Yolk.Email.Composer.Add_From will encode 
 811Name as US_ASCII, instead it means that the data given in Name 
 812already is US_ASCII. So if Name had contained an ISO-8859-1 
 813encoded String, then the call would've looked like this:
 814
 815declare
 816
 817   use Yolk.Email;
 818
 819
 820
 821   Email : Structure;
 822
 823begin
 824
 825   Composer.Add_From      
 826
 827     (ES        => Email,       
 828
 829      Address   => "alice@domain.tld",
 830
 831      Name      => "Alice",
 832
 833      Charset   => ISO_8859_1);
 834
 835end;
 836
 837
 838In this case you will end up with a From header looking like 
 839this:
 840
 841From: =?ISO-8859-1?Q?Alice?= <alice@domain.tld>
 842
 843So bear in mind that it is your responsibility to ensure that 
 844your data, and the Character_Set parameter match.
 845
 846The Recipient_Kind type define the kind of recipient that is 
 847being added to an email. If you've worked with email, these three 
 848should be familiar to you:
 849
 8501. Bcc
 851
 8522. Cc
 853
 8543. To
 855
 856When adding recipients to an email Structure the default is To, 
 857but since not all recipients are equal, you can change the kind 
 858of recipient to Bcc or Cc, according to your needs.
 859
 860The Structure type is at the core of it all. You declare an 
 861object to be of the Structure type, and then you use the 
 862Yolk.Email.Composer facilities to build and send the email.
 863
 86410 Yolk.Email.Composer
 865
 866The actual tools for building and sending an email is found in 
 867this package. Here are tools for building emails from the ground 
 868up and there are a few convenience procedures if you just need to 
 869send a simple email with no bells or whistles.
 870
 871I'm not going to go through ever procedure in this package, 
 872instead I'll show an example on how to build an email from the 
 873ground up and how to use one of the convenience procedures.
 874
 87510.1 Building and sending an email, the easy way
 876
 877There are two convenience procedures in Yolk.Email.Composer for 
 878sending emails without having to do a whole lot of work/thinking. 
 879They are both named Send and they look like this:
 880
 881procedure Send      
 882
 883  (ES             : in out Structure;       
 884
 885   From_Address   : in     String;       
 886
 887   From_Name      : in     String := "";       
 888
 889   To_Address     : in     String;       
 890
 891   To_Name        : in     String := "";       
 892
 893   Subject        : in     String;       
 894
 895   Text_Part      : in     String;       
 896
 897   SMTP_Server    : in     String := "localhost";       
 898
 899   SMTP_Port      : in     Positive := 25;       
 900
 901   Charset        : in     Character_Set := US_ASCII);    
 902
 903   
 904
 905procedure Send      
 906
 907  (ES             : in out Structure;       
 908
 909   From_Address   : in     String;       
 910
 911   From_Name      : in     String := "";       
 912
 913   To_Address     : in     String;       
 914
 915   To_Name        : in     String := "";       
 916
 917   Subject        : in     String;       
 918
 919   Text_Part      : in     String;       
 920
 921   HTML_Part      : in     String;       
 922
 923   SMTP_Server    : in     String := "localhost";       
 924
 925   SMTP_Port      : in     Positive := 25;       
 926
 927   Charset        : in     Character_Set := US_ASCII);
 928
 929
 930As you can see, the only difference between these two is that the 
 931first one sends plain text emails, while the second one sends 
 932multipart/alternative with both plain text and HTML parts. Usage 
 933is as simple as:
 934
 935declare
 936
 937   use Yolk.Email;
 938
 939
 940
 941   Email : Structure;
 942
 943begin
 944
 945   Composer.Send (ES           => Email,
 946
 947                  From_Address => "alice@domain.tld",
 948
 949                  From_Name    => "Alice",
 950
 951                  To_Address   => "bob@domain.tld",
 952
 953                  To_Name      => "Bob",
 954
 955                  Subject      => "Is this thing on?",
 956
 957                  Text_Part    => "Hey you!",
 958
 959                  Charset      => ISO_8859_1);
 960
 961               
 962
 963   if Composer.Is_Send (Email) then
 964
 965      --  Success!               
 966
 967   else                   
 968
 969      --  Failure!
 970
 971   end if;
 972
 973end;
 974
 975
 976It is possible, and allowed, to call some of the various other 
 977procedures prior to calling one of the convenience procedures. If 
 978for example you want to add a custom header, it can be done like 
 979this:
 980
 981declare
 982
 983   use Yolk.Email;
 984
 985
 986
 987   Email : Structure;
 988
 989begin
 990
 991   Composer.Add_Custom_Header (ES      => Email,
 992
 993                               Name    => "User-Agent",
 994
 995                               Value   => "My User Agent");
 996
 997
 998
 999   Composer.Send (ES           => Email,
1000
1001                  From_Address => "alice@domain.tld",
1002
1003                  From_Name    => "Alice",
1004
1005                  To_Address   => "bob@domain.tld",
1006
1007                  To_Name      => "Bob",
1008
1009                  Subject      => "Is this thing on?",
1010
1011                  Text_Part    => "Hey you!",
1012
1013                  Charset      => ISO_8859_1);
1014
1015               
1016
1017   if Composer.Is_Send (Email) then
1018
1019      --  Success!               
1020
1021   else                   
1022
1023      --  Failure!
1024
1025   end if;
1026
1027end;
1028
1029
1030And with that, the header User-Agent: is now added to the email:
1031
1032User-Agent: My User Agent
1033
1034It hardly gets any easier than that. Lets move on and see how the 
1035above is accomplished the hard way.
1036
103710.2 Building and sending email, the hard way
1038
1039It is possible to build an email from the ground up, which 
1040obviously allows for a more fine grained control over what is 
1041added. It is also a bit more complicated, but not much. Lets try 
1042and mimick the easy examples, the “hard” way:
1043
1044declare
1045
1046   use Yolk.Email;
1047
1048
1049
1050   Email : Structure;
1051
1052begin
1053
1054   Composer.Add_Custom_Header (ES    => Email,
1055
1056                               Name  => "User-Agent",
1057
1058                               Value => "My User Agent");
1059
1060
1061
1062   Composer.Add_From (ES      => Email,
1063
1064                      Address => "alice@domain.tld",
1065
1066                      Name    => "Alice",
1067
1068                      Charset => ISO_8859_1);
1069
1070
1071
1072   Composer.Add_Recipient (ES      => Email,
1073
1074                           Address => "bob@domain.tld",
1075
1076                           Name    => "Bob");
1077
1078 
1079
1080   Composer.Set_Subject (ES      => Email,
1081
1082                         Subject => "Is this thing on?");
1083
1084
1085
1086   Composer.Set_Text_Part (ES   => Email,
1087
1088                           Part => "Hey you!");
1089
1090
1091
1092   Composer.Add_SMTP_Server (ES   => Email,
1093
1094                             Host => "localhost");
1095
1096                           
1097
1098   Composer.Send (ES => Email);
1099
1100               
1101
1102   if Composer.Is_Send (Email) then
1103
1104      --  Success!               
1105
1106   else                   
1107
1108      --  Failure!
1109
1110   end if;
1111
1112end;
1113
1114
1115Harder yes, but really not all that much more difficult.
1116
111711 Yolk.Handlers
1118
1119Most web applications will need to handle static content, such as 
1120PNG, HTML and CSS files. Yolk.Handlers helps you accomplish that, 
1121so you don't have to build your own handlers for these kinds of 
1122files.
1123
1124The following filetypes are supported by Yolk.Handlers:
1125
1126• CSS
1127
1128• GIF
1129
1130• HTML
1131
1132• ICO
1133
1134• JPG
1135
1136• JS
1137
1138• PNG
1139
1140• SVG
1141
1142• XML
1143
1144• XSL
1145
1146The filetypes that are textual, are compressed according to the 
1147Yolk.Configuration parameter Compress_Static_Content which 
1148defaults to False. The regular expressions for identifying these 
1149filetypes are also defined in Yolk.Configuration by the Handler_* 
1150parameters. These regular expressions are registered by the 
1151AWS.Services.Dispatchers.URI.Register_Regexp procedure.
1152
1153There's only one procedure in the Yolk.Handlers package:
1154
1155procedure Set (RH : out AWS.Services.Dispatchers.URI.Handler);
1156
1157
1158You can see an example on how this is used in the demo file 
1159my_handlers.adb. There's really very little reason not to use 
1160this package for handling of static content, but it is of course 
1161not mandatory.
1162
1163This package makes use of the Yolk.Static_Content package for the 
1164actual delivery of the content to the user.
1165
116612 Yolk.Log
1167
1168This package serves two purposes:
1169
11701. It contains the two callback procedures used to write AWS 
1171  logging data (access and error) to syslogd.
1172
11732. It creates the SQL, SQL_Cache, SQL_Error, SQL_Select, Alert, 
1174  Critical, Debug, Emergency, Error, Info, Notice and Warning 
1175  trace handles, and activates them according to the values 
1176  defined in the configuration file.
1177
1178Out of the box, a Yolk application requires a running syslogd 
1179daemon, as all log data is sent to syslogd. If for some reason 
1180you don't want to use syslogd, you're going to have to hack the 
1181Yolk.Log package, or remove it entirely.
1182
1183The two procedures named AWS_* are used by the AWS HTTP(S) 
1184server. These should not be used for anything else - or rather: 
1185If you use them for anything else, the Message given is going to 
1186be written to syslogd with either the AWS access log label or AWS 
1187error log label. There's absolutely no harm in this, except it 
1188might be a bit confusing when reading the log data.
1189
1190The Trace procedure is the one that you will be using in your 
1191application. You can send log data to the trace handles defined 
1192in Yolk.Log.Trace_Handles. All log data is then sent to the 
1193syslogd daemon using the facilities set in the configuration file 
1194for the given trace handle.
1195
1196Using Trace is about as easy as it gets:
1197
1198Yolk.Log.Trace (Handle  => Error,
1199
1200                Message => "Secret sauce to Error!");
1201
1202
1203If you haven't activated a trace handle, then calling Trace for 
1204that handle does nothing, ie. you don't have to remove all your 
1205Trace calls from the code if you don't use them.
1206
1207Note that on many systems the Emergency messages are written to 
1208console, so be cautious about using this specific level, unless 
1209you've made certain that you can get to the messages.
1210
121113 Yolk.Not_Found
1212
1213The job of this package is to return a HTTP 404 status code and 
1214an accompanying simple not found HTML page. It's sole function 
1215Generate is about as simple as they come:
1216
1217function Generate      
1218
1219  (Request : in AWS.Status.Data)       
1220
1221   return AWS.Response.Data;
1222
1223
1224It relies on the template file demo/exe/templates/system/404.tmpl 
1225to generate the generic 404 HTML page, so if you want to use 
1226Yolk.Not_Found in your own application, then remember to bring 
1227along this file. Where the 404.tmpl is placed is defined in the 
1228configuration parameter System_Templates_Path.
1229
1230Also worth noting is that the Yolk.Not_Found.Generate function is 
1231used as the default callback in the demo application. This means 
1232that all requested resources that doesn't match a registered 
1233dispatcher, is served by Yolk.Not_Found.Generate ie. a 404 is 
1234returned. See the demo/src/my_handlers.adb file for more 
1235information.
1236
123714 Yolk.Process_Control
1238
1239With Yolk.Process_Control you get the ability to control your 
1240application using the SIGINT, SIGPWR and SIGTERM signals. If you 
1241give your Yolk application the --pid-file commandline argument 
1242when starting it, Yolk.Process_Control will create a PID file on 
1243the given location, if it is allowed to do so. This PID file will 
1244also be deleted when the application terminates. Note that the 
1245extras/rc.yolk script handles all this transparently.
1246
124714.1 Exceptions
1248
1249These are:
1250
1251• Cannot_Create_PID_File. Is raised if the PID_File cannot be 
1252  created, eg. if the application lacks permissions to write to 
1253  the directory where the PID_File is located.
1254
1255• Cannot_Delete_PID_File. Is raised if the PID_file cannot be 
1256  deleted, eg. if the application lacks permissions to write to 
1257  the directory where the PID_File is located, or to the PID_File 
1258  itself.
1259
1260• PID_File_Exists. Is raised when the PID_File already exists, 
1261  ie. the application is already running or it was shutdown 
1262  incorrectly.
1263
126414.2 Using Yolk.Process_Control
1265
1266When you use the Yolk.Process_Control package the 
1267Unreserve_All_Interrupts pragma is used. This means that 
1268depending on the compiler used one or more interrupt signals may 
1269be affected. In the case of the GNAT compiler, this is 
1270specifically mentioned in the source of the Ada.Interrupts.Names 
1271package:
1272
1273-- The pragma Unreserve_All_Interrupts affects the following 
1274signal(s):
1275
1276-- SIGINT: made available for Ada handler
1277
1278Since neither SIGPWR or SIGTERM are reserved by the compiler, the 
1279Yolk.Process_Control package is able to assume control of these 
1280signals. You can read more about the [http://gcc.gnu.org/onlinedocs/gnat_rm/Pragma-Unreserve_005fAll_005fInterrupts.html||pragma Unreserve_All_Interrupts here]
1281. If you compile Yolk with a different compiler than GNAT, then 
1282please check if one of the affected signals are reserved.
1283
1284There are two procedures in the Yolk.Process_Control package:
1285
1286procedure Stop;
1287
1288
1289
1290procedure Wait;
1291
1292
1293When you call the Wait procedure, you basically hang there until
1294
12951. The Stop procedure is called
1296
12972. The application receives a SIGINT, SIGPWR or SIGTERM signal
1298
1299This is quite handy for applications, that need some sort of loop 
1300to keep them from terminating. You can see an example on how this 
1301can be done in the demo/src/yolk_demo.adb file.
1302
1303When Wait is called, subsequent calls to Wait are ignored, unless 
1304a call to Stop has been made or the application has received one 
1305of the SIGINT, SIGPWR or SIGTERM signals. So it's perfectly valid 
1306to do:
1307
1308Wait;
1309
1310--  Stop called from somewhere in the app
1311
1312--  Do something...
1313
1314Wait;
1315
1316--  The app receives a SIGINT signal
1317
1318--  Do something...
1319
1320Wait;
1321
1322
1323Whether or not this is actually useful I don't know, but it is 
1324possible.
1325
132615 Yolk.Process_Owner
1327
1328When it is necessary to change the owner of a process, the 
1329Yolk.Process_Owner package is the solution. Obviously this can 
1330also be done when starting the application, using various shell 
1331tricks, but I find it it much cleaner to just let the application 
1332handle it by itself.
1333
133415.1 Exceptions
1335
1336There's only one:
1337
13381. Username_Does_Not_Exist. This is raised if the given username 
1339  doesn't exist on the system.
1340
134115.2 Using Yolk.Process_Owner
1342
1343There's only a single procedure in this package and its 
1344specification looks like this:
1345
1346procedure Set_User      
1347
1348  (Username : in String);    
1349
1350  --  Set the process owner to Username.
1351
1352
1353Please note that when changing the user ID of the application 
1354with Set_User, the group ID is changed to the first group the 
1355given user is a member of.
1356
1357Usage is as simple as expected:
1358
1359declare
1360
1361begin
1362
1363   Set_User (Username => "billybob");
1364
1365exception
1366
1367   when Username_Does_Not_Exist =>
1368
1369      --  User is missing. Do something!
1370
1371end;
1372
1373
1374In the file demo/src/yolk_demo.adb you'll find that 
1375Yolk.Process_Owner.Set_User is used in conjunction with the 
1376Yolk.Configuration.Yolk_User parameter.
1377
137816 Yolk.Server
1379
1380This is a convenience package to handle creating, starting and 
1381stopping an AWS HTTP server. The biggest drawback to this package 
1382is that it can only create and manage one AWS server.
1383
1384Note a potential manual operation when using the Yolk.Server 
1385package : Setting the Cache-Control header. This can become 
1386necessary if you
1387
13881. use Yolk.Server to create and start your AWS server AND
1389
13902. use the Yolk.Handlers package to register dispatchers for 
1391  static content AND
1392
13933. have the Compress_Static_Content configuration parameter set 
1394  to True AND
1395
13964. aren't content with the default Cache-Control header
1397
1398In that case you can use the 
1399Yolk.Static_Content.Set_Cache_Options procedure to work your own 
1400magic on the Cache-Control header sent to clients requesting 
1401static content. See the Yolk.Static_Content chapter for more 
1402information.
1403
140416.1 Yolk.Server.Create
1405
1406Creating a server is done by calling the Create function. This 
1407function accepts one parameter that should be fairly 
1408self-explanatory.
1409
1410type HTTP is tagged limited private;
1411
1412
1413
1414function Create
1415
1416  (Unexpected : in AWS.Exceptions.Unexpected_Exception_Handler)   
1417    
1418
1419   return HTTP;    
1420
1421
142216.2 Yolk.Server.Start
1423
1424procedure Start 
1425
1426  (WS          : in out HTTP;
1427
1428   Dispatchers : in     AWS.Dispatchers.Handler'Class);
1429
1430
1431This does as expected: Starts the server with Dispatchers.
1432
1433When calling Start several things happen:
1434
14351. If the configuration parameter Load_MIME_Types_File is True 
1436  then the MIME types file given in MIME_Types_File is loaded 
1437  into AWS. 
1438
14392. If the configuration parameter Start_WebSocket_Servers is True 
1440  then the AWS WebSocket servers are started.
1441
14423. Yolk.Static_Content.Static_Content_Cache_Setup is called with 
1443  its default parameters if the configuration parameter 
1444  Compress_Static_Content is True.
1445
14464. If session support is turned on for the server and the 
1447  Session_Data_File exists, then the session data is loaded from 
1448  this file.
1449
14505. The Yolk.Log.AWS_Access_Log_Writer procedure is registered to 
1451  handle all AWS access log data if the configuration parameter 
1452  AWS_Access_Log_Activate is True .
1453
14546. The Yolk.Log.AWS_Error_Log_Writer procedure is registered to 
1455  handle all AWS error log data if the configuration parameter 
1456  AWS_Error_Log_Activate is True .
1457
145816.3 Yolk.Server.Stop
1459
1460procedure Stop      
1461
1462  (WS : in out HTTP);
1463
1464
1465This does as expected: Stops everything that was started when the 
1466Yolk.Server.Start call was made. Note that if sessions support is 
1467turned on for the server, then the Dispatchers are switched for a 
1468plain Not Found dispatcher when Stop is called. This is done to 
1469prevent session tampering while the server is shutting down.
1470
147117 Yolk.Static_Content
1472
1473Most web applications have a lot of static content, ie. stuff 
1474that rarely changes. Things like PNG's, HTML, CSS and Javascript. 
1475These are content types that are common for most websites, so a 
1476application is going to have to handle these in some way. This is 
1477where Yolk.Static_Content comes in. Two kinds of files are 
1478handled by Yolk.Static_Content:
1479
1480• Compressable (XML, HTML, CSS, JS and so on)
1481
1482• Non-compressable (PNG, JPG, GIF, ICO and so on)
1483
1484It is up to you, the user, to decide whether a specific kind of 
1485file is compressable or not - the package does not make any such 
1486assumptions. The difference between the two, is that compressable 
1487files are compressed prior to delivery, if the clients HTTP 
1488request includes a Accept-Encoding: gzip header. Non-compressable 
1489files are simply returned as is. For both kinds, a generic 404 is 
1490returned if the requested file doesn't exist.
1491
1492Yolk.Static_Content is affected by five configuration settings:
1493
1494• Compress_Static_Content
1495
1496• Compress_Static_Content_Minimum_File_Size
1497
1498• Compressed_Static_Content_Cache
1499
1500• Compressed_Static_Content_Max_Age
1501
1502• WWW_Root
1503
1504You should carefully read the demo/exe/configuration/config.ini 
1505file for information on what exactly these do.
1506
150717.1 Yolk.Static_Content.Static_Content_Cache_Setup
1508
1509The configuration parameter Compressed_Static_Content_Cache 
1510defines where the compressed version of the static content is 
1511saved. When your application is started, this directory may or 
1512may not exist, and it may or may not contain various compressed 
1513files. To make sure that the directory exists and is empty, you 
1514should call the Static_Content_Cache_Setup procedure:
1515
1516procedure Static_Content_Cache_Setup      
1517
1518  (No_Cache          : in Boolean := False;       
1519
1520   No_Store          : in Boolean := False;       
1521
1522   No_Transform      : in Boolean := False;       
1523
1524   Max_Age           : in AWS.Messages.Delta_Seconds := 86400;
1525
1526   S_Max_Age         : in AWS.Messages.Delta_Seconds := 
1527
1528     AWS.Messages.Unset;
1529
1530   Public            : in Boolean := False;       
1531
1532   Must_Revalidate   : in Boolean := True;       
1533
1534   Proxy_Revalidate  : in Boolean := False);
1535
1536
1537This procedure creates the Compressed_Static_Content_Cache 
1538directory if it doesn't already exists and if it exists, it 
1539deletes everything in it. So basically you're left with an empty 
1540directory after a call to this procedure. 
1541
1542The parameters defines the content of the Cache-Control header 
1543sent to the user agent when a request for static content is made. 
1544The default parameters are fairly sane, and will result in a 
1545cache header looking like this:
1546
1547Cache-Control: max-age=86400, must-revalidate
1548
1549
1550Read the [http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9||HTTP/1.1 Cache-Control header definition]
1551 for more information on what the various settings do.
1552
1553If you use Yolk.Server to create and start your AWS server, then 
1554this called is made automatically with it's default parameters. 
1555
1556If you don't use Yolk.Server and you use the Yolk.Static_Content 
1557package to serve static content, then you must call this 
1558procedure before starting the AWS server. Calling it repeatedly 
1559will simply wipe out the compressed cache directory and reset the 
1560Control_Cache header according to the given parameters.
1561
156217.2 Yolk.Static_Content.Compressable
1563
1564If your application contains a bunch of compressable files of 
1565significant size, you can save a lot of bandwidth by using the 
1566Compressable function to serve them:
1567
1568function Compressable      
1569
1570  (Request : in AWS.Status.Data)       
1571
1572   return AWS.Response.Data;
1573
1574
1575A natural place to call this function would be where you define 
1576your content handlers, as seen in this, slightly altered, excerpt 
1577from the Yolk.Handlers package:
1578
1579declare
1580
1581   package SC renames Yolk.Static_Content;
1582
1583   Resource_Handlers : AWS.Services.Dispatchers.URI.Handler;
1584
1585begin
1586
1587   AWS.Services.Dispatchers.URI.Register_Regexp         
1588
1589     (Dispatcher  => Resource_Handlers,          
1590
1591      URI         => "\.css$",          
1592
1593      Action      => Create (Callback => 
1594SC.Compressable'Access));
1595
1596
1597
1598   AWS.Services.Dispatchers.URI.Register_Regexp         
1599
1600     (Dispatcher  => Resource_Handlers,          
1601
1602      URI         => "\.html$",          
1603
1604      Action      => Create (Callback => 
1605SC.Compressable'Access));
1606
1607end;
1608
1609
1610Here you can see that we've defined some content handlers for CSS 
1611and HTML files based on a few simple regular expressions, and 
1612whenever a resource is requested that matches one of these, the 
1613corresponding Action callback is called, which in this example is 
1614the Compressable function. The following steps are then taken:
1615
16161. Does the requested resource exist and is it an ordinary file?
1617
1618  (a) If no, then return a 404 message
1619
1620  (b) if yes, then proceed to 2
1621
16222. Does the client support compressed content?
1623
1624  (a) If no, then return the requested resource un-compressed
1625
1626  (b) if yes, then proceed to 3
1627
16283. Is there a compressed version of the resource available on 
1629  disk?
1630
1631  (a) if no, then make one, cache it for future use, and return 
1632    it
1633
1634  (b) If yes, then proceed to 4
1635
16364. Is the age of the compressed file <= Compressed_Max_Age?
1637
1638  (a) If no, then delete the current file, build a new one, cache 
1639    it and return it
1640
1641  (b) If yes, then return it
1642
1643And that's really all there is to it. Note that Compressable 
1644always looks for requested content in the WWW_Root directory, so 
1645if the user requests the file /css/index.css, then the path is 
1646going to be:
1647
1648WWW_Root & "/css/index.ss";
1649
1650
1651Using the default configuration value for WWW_Root, we'll end up 
1652with the path:
1653
1654static_content/css/index.css
1655
1656The compressed version of index.css is saved as:
1657
1658Compressed_Static_Content_Cache & "/css/index.css" & ".gz";
1659
1660
1661Using the default configuration value for 
1662Compressed_Static_Content_Cache, we'll end up with the path:
1663
1664static_content/compressed_cache/css/index.css.gz
1665
1666In these two cases the paths are relative to the executable, but 
1667you can of course also define WWW_Root and 
1668Compressed_Static_Content_Cache as absolute paths in the 
1669configuration file.
1670
167117.3 Yolk.Static_Content.Non_Compressable
1672
1673A lot of static content really doesn't benefit from any further 
1674compression. This is the case for PNG files, JPEG files and a 
1675whole lot of other kinds of files. Static content that has 
1676already been compressed, should be handled by the 
1677Non_Compressable function:
1678
1679function Non_Compressable      
1680
1681  (Request : in AWS.Status.Data)       
1682
1683   return AWS.Response.Data;
1684
1685
1686A call to Non_Compressable is much simpler compared to a call to 
1687it's sibling function Compressable:
1688
16891. Does the requested resource exist and is it an ordinary file?
1690
1691  (a) If no, then return a 404 message
1692
1693  (b) if yes, then return the file
1694
1695And that's it. Either the resource is there, or it isn't. If the 
1696requested resource is /images/foo.png, then the Non_Compressable 
1697function searches for the resource in:
1698
1699WWW_Root & "/images/foo.png";
1700
1701
1702Using the default configuration value for WWW_Root, we'll end up 
1703with the path:
1704
1705static_content/images/foo.png
1706
1707Just as with Compressable a natural place to use Non_Compressable 
1708is where you define you content handlers, but it can obviously be 
1709used wherever your application handles requests for static 
1710content.
1711
171217.4 Yolk.Static.Set_Cache_Options
1713
1714You can alter the Cache-Control header sent to clients requesting 
1715static content with a call to Set_Cache_Options. This is 
1716especially handy if you use the Yolk.Server package to create and 
1717start your server, since the initial call to 
1718Static_Content_Cache_Setup is hidden you're forced to live with 
1719the default values given in the Static_Content_Cache_Setup call, 
1720resulting in a Cache-Control header looking like this:
1721
1722Cache-Control: max-age=86400, must-revalidate
1723
1724
1725If this is not what you want, you can use Set_Cache_Options to 
1726change it:
1727
1728procedure Set_Cache_Options
1729
1730  (No_Cache          : in Boolean := False;       
1731
1732   No_Store          : in Boolean := False;       
1733
1734   No_Transform      : in Boolean := False;      
1735
1736   Max_Age           : in AWS.Messages.Delta_Seconds := 86400;    
1737   
1738
1739   S_Max_Age         : in AWS.Messages.Delta_Seconds := 
1740
1741     AWS.Messages.Unset;       
1742
1743   Public            : in Boolean := False;       
1744
1745   Must_Revalidate   : in Boolean := True;       
1746
1747   Proxy_Revalidate  : in Boolean := False);
1748
1749
1750This MUST be called immediately after having called 
1751Yolk.Server.Start, since the call to Static_Content_Cache_Setup 
1752in Start will overwrite the cache options.
1753
175418 Yolk.Syndication
1755
1756The Atom Syndication format ([http://tools.ietf.org/html/rfc4287||RFC4287]
1757) is an XML-based document format that describes lists of related 
1758information known as "feeds". Atom documents are used by many web 
1759applications as a means of publishing information to users. It's 
1760not a complicated format, but it does require a bit of work to 
1761construct a proper Atom feed by hand, and since I try my best to 
1762avoid work, I made the Yolk.Syndication package. This package 
1763makes it “easy” to put together an Atom feed document, and have 
1764it delivered as a string or an XML/Ada DOM object.
1765
1766Yolk.Syndication helps you construct the Atom XML - it does not 
1767check that the resulting XML is valid Atom XML, ie. that the Atom 
1768rules have been followed, so if the Atom specification says that
1769
1770atom:feed elements MUST contain exactly one atom:id element.
1771
1772then it is your job to make sure that your Atom feed has exactly 
1773one such element. So before venturing into the realm of Atom, 
1774it's probably a good idea to read [http://tools.ietf.org/html/rfc4287||RFC4287]
1775 for a good understanding of the requirements for this document 
1776format. A basic example of how to build an Atom feed can be found 
1777in the Yolk demo application.
1778
1779There are two packages in the Yolk.Syndication hierarchy:
1780
1781• Yolk.Syndication - Here the necessary types and exceptions are 
1782  defined.
1783
1784• Yolk.Syndication.Writer - Here the functions and procedures for 
1785  actually creating an Atom feed are defined.
1786
1787When time permits, I'll add a Yolk.Syndication.Reader package for 
1788extraction of data from an Atom feed.
1789
179018.1 Exceptions
1791
1792There's one exception in this package:
1793
1794• Not_Valid_XML. Is raised when some Xhtml content is not valid 
1795  XML. This exception can be raised by all procedures that takes 
1796  Xhtml as content.
1797
179818.2 The Yolk.Syndication types
1799
1800There are 5 important types in this package:
1801
1802• Text_Kinds
1803
1804• Relation_Kinds
1805
1806• Atom_Entry
1807
1808• Atom_Entry_Source
1809
1810• Atom_Feed
1811
1812A few of the procedures in Yolk.Syndication.Writer has parameters 
1813of the Text_Kinds type. This identifies the kind of data that is 
1814being added, with possible values being 
1815
1816• Text 
1817
1818• Html
1819
1820• Xhtml
1821
1822Hopefully it's obvious what each of these refers to. Procedures 
1823that have parameters of the Text_Kinds type always add data to 
1824the feed that can be interpreted as one of these three kinds of 
1825text.
1826
1827The Relation_Kinds type is used in correlation with links that 
1828are added to the feed. It identifies how the link relates to the 
1829current feed/entry. There are 5 possible relations:
1830
1831• Alternate: Signifies that the link points to an alternate 
1832  version of the resource described by the containing element.
1833
1834• Related: Signifies that the link points to a resource that is 
1835  related to, but not the same as, the resource described by the 
1836  containing element.
1837
1838• Self: Sig…

Large files files are truncated, but you can click here to view the full file