sws 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/doc/DOC.otl +34 -0
  2. data/doc/Makefile +13 -0
  3. data/doc/architecture.dia +0 -0
  4. data/doc/docbook/architecture.png +0 -0
  5. data/doc/docbook/concepts.docbook +474 -0
  6. data/doc/docbook/installation.docbook +57 -0
  7. data/doc/docbook/introduction.docbook +130 -0
  8. data/doc/docbook/sws_manual.docbook +35 -0
  9. data/doc/docbook/todo.docbook +38 -0
  10. data/doc/docbook/tutorial.docbook +594 -0
  11. data/examples/README +1 -0
  12. data/examples/addressbook/CardBrowse/CardBrowse.html +43 -0
  13. data/examples/addressbook/CardBrowse/CardBrowse.rb +65 -0
  14. data/examples/addressbook/CardBrowse/CardBrowse.sws +92 -0
  15. data/examples/addressbook/Login/LoginPage.html +12 -0
  16. data/examples/addressbook/Login/LoginPage.rb +19 -0
  17. data/examples/addressbook/Login/LoginPage.sws +15 -0
  18. data/examples/addressbook/README +1 -0
  19. data/examples/addressbook/addressbook.rb +70 -0
  20. data/examples/addressbook/application.yaml +8 -0
  21. data/examples/addressbook/db.yaml +7 -0
  22. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.html +11 -0
  23. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.rb +21 -0
  24. data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.sws +25 -0
  25. data/examples/component_demo/ComponentDemo.rb +21 -0
  26. data/examples/component_demo/ConditionalDemo/ConditionalDemo.html +18 -0
  27. data/examples/component_demo/ConditionalDemo/ConditionalDemo.rb +2 -0
  28. data/examples/component_demo/ConditionalDemo/ConditionalDemo.sws +22 -0
  29. data/examples/component_demo/FileUploadDemo/FileUploadDemo.html +10 -0
  30. data/examples/component_demo/FileUploadDemo/FileUploadDemo.rb +9 -0
  31. data/examples/component_demo/FileUploadDemo/FileUploadDemo.sws +33 -0
  32. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.html +12 -0
  33. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.rb +21 -0
  34. data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.sws +40 -0
  35. data/examples/component_demo/FormListsDemo/FormListsDemo.html +11 -0
  36. data/examples/component_demo/FormListsDemo/FormListsDemo.rb +37 -0
  37. data/examples/component_demo/FormListsDemo/FormListsDemo.sws +47 -0
  38. data/examples/component_demo/GenericDemo/GenericDemo.html +4 -0
  39. data/examples/component_demo/GenericDemo/GenericDemo.rb +2 -0
  40. data/examples/component_demo/GenericDemo/GenericDemo.sws +10 -0
  41. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.html +8 -0
  42. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.rb +20 -0
  43. data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.sws +19 -0
  44. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.html +11 -0
  45. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.rb +2 -0
  46. data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.sws +14 -0
  47. data/examples/component_demo/PageWrapper/PageWrapper.html +23 -0
  48. data/examples/component_demo/PageWrapper/PageWrapper.rb +2 -0
  49. data/examples/component_demo/PageWrapper/PageWrapper.sws +42 -0
  50. data/examples/component_demo/README +1 -0
  51. data/examples/component_demo/RepetitionDemo/RepetitionDemo.html +13 -0
  52. data/examples/component_demo/RepetitionDemo/RepetitionDemo.rb +19 -0
  53. data/examples/component_demo/RepetitionDemo/RepetitionDemo.sws +20 -0
  54. data/examples/component_demo/StringDemo/StringDemo.html +5 -0
  55. data/examples/component_demo/StringDemo/StringDemo.rb +16 -0
  56. data/examples/component_demo/StringDemo/StringDemo.sws +14 -0
  57. data/examples/component_demo/application.yaml +28 -0
  58. data/examples/component_demo/poweredby.jpg +0 -0
  59. data/examples/component_demo/style.css +1 -0
  60. data/examples/movies/Menu/Menu.html +3 -0
  61. data/examples/movies/Menu/Menu.rb +7 -0
  62. data/examples/movies/Menu/Menu.sws +7 -0
  63. data/examples/movies/MovieBrowse/MovieBrowse.html +68 -0
  64. data/examples/movies/MovieBrowse/MovieBrowse.rb +178 -0
  65. data/examples/movies/MovieBrowse/MovieBrowse.sws +127 -0
  66. data/examples/movies/README +1 -0
  67. data/examples/movies/UserBrowse/UserBrowse.html +50 -0
  68. data/examples/movies/UserBrowse/UserBrowse.rb +69 -0
  69. data/examples/movies/UserBrowse/UserBrowse.sws +49 -0
  70. data/examples/movies/application.yaml +11 -0
  71. data/examples/movies/da.rb +36 -0
  72. data/examples/movies/dbmovies.rb +44 -0
  73. data/examples/movies/frameworks/TestFramework/framework.yaml +4 -0
  74. data/examples/movies/frameworks/TestFramework/resources/im1.jpg +0 -0
  75. data/examples/movies/images/pbr1b.jpg +0 -0
  76. data/examples/movies/movies.rb +28 -0
  77. data/examples/movies/movies.sds +119 -0
  78. data/examples/movies/movies.sqlite +0 -0
  79. data/examples/movies/movies_mysql.sql +28 -0
  80. data/examples/movies/movies_postgres.sql +27 -0
  81. data/examples/movies/movies_sqlite.sql +28 -0
  82. data/lib/sws.rb +89 -0
  83. data/lib/sws/Core/components/CheckBox/CheckBox.api +5 -0
  84. data/lib/sws/Core/components/CheckBox/CheckBox.rb +45 -0
  85. data/lib/sws/Core/components/CheckBoxList/CheckBoxList.api +13 -0
  86. data/lib/sws/Core/components/CheckBoxList/CheckBoxList.rb +54 -0
  87. data/lib/sws/Core/components/Conditional/Conditional.api +3 -0
  88. data/lib/sws/Core/components/Conditional/Conditional.html +1 -0
  89. data/lib/sws/Core/components/Conditional/Conditional.rb +39 -0
  90. data/lib/sws/Core/components/Conditional/Conditional.sws +2 -0
  91. data/lib/sws/Core/components/Content/Content.rb +18 -0
  92. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.html +13 -0
  93. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.rb +18 -0
  94. data/lib/sws/Core/components/ExceptionPage/ExceptionPage.sws +16 -0
  95. data/lib/sws/Core/components/FileUpload/FileUpload.api +16 -0
  96. data/lib/sws/Core/components/FileUpload/FileUpload.rb +62 -0
  97. data/lib/sws/Core/components/Form/Form.api +9 -0
  98. data/lib/sws/Core/components/Form/Form.html +3 -0
  99. data/lib/sws/Core/components/Form/Form.rb +55 -0
  100. data/lib/sws/Core/components/Form/Form.sws +12 -0
  101. data/lib/sws/Core/components/GenericContainer/GenericContainer.api +10 -0
  102. data/lib/sws/Core/components/GenericContainer/GenericContainer.html +1 -0
  103. data/lib/sws/Core/components/GenericContainer/GenericContainer.rb +39 -0
  104. data/lib/sws/Core/components/GenericContainer/GenericContainer.sws +12 -0
  105. data/lib/sws/Core/components/GenericElement/GenericElement.api +10 -0
  106. data/lib/sws/Core/components/GenericElement/GenericElement.rb +34 -0
  107. data/lib/sws/Core/components/HiddenField/HiddenField.api +7 -0
  108. data/lib/sws/Core/components/HiddenField/HiddenField.rb +37 -0
  109. data/lib/sws/Core/components/Hyperlink/Hyperlink.api +13 -0
  110. data/lib/sws/Core/components/Hyperlink/Hyperlink.html +1 -0
  111. data/lib/sws/Core/components/Hyperlink/Hyperlink.rb +102 -0
  112. data/lib/sws/Core/components/Hyperlink/Hyperlink.sws +12 -0
  113. data/lib/sws/Core/components/Image/Image.api +11 -0
  114. data/lib/sws/Core/components/Image/Image.rb +49 -0
  115. data/lib/sws/Core/components/ImageButton/ImageButton.api +16 -0
  116. data/lib/sws/Core/components/ImageButton/ImageButton.rb +76 -0
  117. data/lib/sws/Core/components/Link/Link.api +11 -0
  118. data/lib/sws/Core/components/Link/Link.rb +39 -0
  119. data/lib/sws/Core/components/PasswordField/PasswordField.api +7 -0
  120. data/lib/sws/Core/components/PasswordField/PasswordField.rb +41 -0
  121. data/lib/sws/Core/components/RadioButton/RadioButton.api +7 -0
  122. data/lib/sws/Core/components/RadioButton/RadioButton.rb +44 -0
  123. data/lib/sws/Core/components/RadioButtonList/RadioButtonList.api +20 -0
  124. data/lib/sws/Core/components/RadioButtonList/RadioButtonList.rb +76 -0
  125. data/lib/sws/Core/components/Repetition/Repetition.api +10 -0
  126. data/lib/sws/Core/components/Repetition/Repetition.html +1 -0
  127. data/lib/sws/Core/components/Repetition/Repetition.rb +137 -0
  128. data/lib/sws/Core/components/Repetition/Repetition.sws +2 -0
  129. data/lib/sws/Core/components/ResetButton/ResetButton.api +5 -0
  130. data/lib/sws/Core/components/ResetButton/ResetButton.rb +28 -0
  131. data/lib/sws/Core/components/Select/Select.api +24 -0
  132. data/lib/sws/Core/components/Select/Select.rb +57 -0
  133. data/lib/sws/Core/components/String/String.api +5 -0
  134. data/lib/sws/Core/components/String/String.rb +28 -0
  135. data/lib/sws/Core/components/SubmitButton/SubmitButton.api +6 -0
  136. data/lib/sws/Core/components/SubmitButton/SubmitButton.rb +53 -0
  137. data/lib/sws/Core/components/TextArea/TextArea.api +9 -0
  138. data/lib/sws/Core/components/TextArea/TextArea.rb +28 -0
  139. data/lib/sws/Core/components/TextField/TextField.api +9 -0
  140. data/lib/sws/Core/components/TextField/TextField.rb +46 -0
  141. data/lib/sws/Core/framework.yaml +25 -0
  142. data/lib/sws/JSComponents/components/JSMenu/JSMenu.api +5 -0
  143. data/lib/sws/JSComponents/components/JSMenu/JSMenu.html +58 -0
  144. data/lib/sws/JSComponents/components/JSMenu/JSMenu.rb +34 -0
  145. data/lib/sws/JSComponents/components/JSMenu/JSMenu.sws +37 -0
  146. data/lib/sws/JSComponents/framework.yaml +3 -0
  147. data/lib/sws/adaptor.rb +334 -0
  148. data/lib/sws/application.rb +604 -0
  149. data/lib/sws/component.rb +656 -0
  150. data/lib/sws/cookie.rb +27 -0
  151. data/lib/sws/direct_action.rb +38 -0
  152. data/lib/sws/extensions.rb +49 -0
  153. data/lib/sws/parsers.rb +374 -0
  154. data/lib/sws/request.rb +308 -0
  155. data/lib/sws/response.rb +70 -0
  156. data/lib/sws/session.rb +195 -0
  157. data/lib/sws/slot.rb +198 -0
  158. metadata +263 -0
@@ -0,0 +1,57 @@
1
+ <chapter id="installation">
2
+ <title>Obtaining & installing SWS</title>
3
+
4
+ <sect1 id="installation-where-to-get">
5
+ <title>Where to get SWS?</title>
6
+
7
+ <para>You can get latest release of SWS from <ulink url="http://starware.one.pl/software/download/sws/sws-latest.tar.gz">http://starware.one.pl/software/download/sws/sws-latest.tar.gz</ulink>. The RubyGem for SWS can be obtained from <ulink url="http://starware.one.pl/software/download/sws/sws-latest.gem">http://starware.one.pl/software/download/sws/sws-latest.gem</ulink></para>
8
+
9
+ </sect1>
10
+
11
+ <sect1 id="installation-installation">
12
+ <title>Installation</title>
13
+
14
+ <para>The preferred way to install SWS is to use RubyGem. Just get the SWS gem and issue a command</para>
15
+ <programlisting>
16
+ gem -i sws-latest.gem
17
+ </programlisting>
18
+ <para>or install the gem remotely</para>
19
+ <programlisting>
20
+ gem -i sws
21
+ </programlisting>
22
+
23
+ <para>Alternatively you may just use setup.rb included in SWS tarball. Just run</para>
24
+ <programlisting>
25
+ ruby setup.rb config
26
+ ruby setup.rb setup
27
+ ruby setup.rb install
28
+ </programlisting>
29
+
30
+ <para>from the unpacked tarball directory.</para>
31
+
32
+ <important><para>
33
+ Remember - using RubyGem is now a preferred way to install SWS. Other ways of installation
34
+ will be dropped in future versions.
35
+ </para></important>
36
+
37
+ </sect1>
38
+
39
+ <sect1 id="installation-upgrade">
40
+ <title>Upgrading from earlier version</title>
41
+
42
+ <sect2 id="installation-upgrade-0.2">
43
+ <title>From version 0.2.1 and above</title>
44
+ <para>
45
+ Just perform the installation as specified above.
46
+ </para>
47
+ </sect2>
48
+
49
+ <sect2 id="installation-upgrade-0.1">
50
+ <title>From versions 0.2 and below</title>
51
+ <para>You need to uninstall previous version - remove <replaceable>sws</replaceable> directory and <replaceable>sws.rb</replaceable> file from <replaceable>site_ruby</replaceable>. Remove <replaceable>data_dir/sws</replaceable> (usually <replaceable>/usr/share/sws</replaceable>) directory too. Then you can install the new version.</para>
52
+ </sect2>
53
+
54
+ </sect1>
55
+
56
+ </chapter>
57
+
@@ -0,0 +1,130 @@
1
+ <chapter id="introduction">
2
+ <title>Introduction</title>
3
+
4
+ <sect1 id="what-is-sws">
5
+ <title>What is SWS?</title>
6
+
7
+ <para>SWS is a web development library for <ulink url="http://www.ruby-lang.org">Ruby programming language</ulink>. Its major design goal is to follow the Model-View-Controller pattern, to take the burden of parsing HTTP parameters and keeping the session information out of developer shoulders and to make web applications easier to write by using reusable components. It enables rapid development and easy maintaining of even complex web applications, such as database ones (together with database access library - <ulink url="http://www.starware.one.pl/software/sds/">SDS</ulink>.</para>
8
+ <para>SWS is modeled after Apple excellent <ulink url="http://www.apple.com/webobjects">WebObjects</ulink> web development environment. There is another Ruby library modelled after it - <ulink url="http://www.spice-of-life.net/download/cgikit/index_en.html">CGIKit</ulink> - you may want to check it out.</para>
9
+
10
+ </sect1>
11
+
12
+ <sect1 id="why-write-sws">
13
+ <title>Why another web development library?</title>
14
+ <itemizedlist>
15
+ <listitem><para>SWS is modelled after WebObjects, which still (despite its age) is one of the best designed and most developer-friendly web development library</para></listitem>
16
+ <listitem><para>SWS is designed to be used in cooperation with <ulink url="http://www.starware.one.pl/software/sds/">SDS</ulink> - a powerful database access and object persistence library, so it provides the major components you need to develop serious business applications</para></listitem>
17
+ <listitem><para>SWS follows strictly the Model-View-Controller pattern, so the code is clear and can be easy maintained</para></listitem>
18
+ <listitem><para>Session handling has some very powerful features and is performed with no additional work for the developer</para></listitem>
19
+ <listitem><para>With SWS you no longer need to parse HTTP parameters - they are automatically parsed and bound to appropiate variables</para></listitem>
20
+ <listitem><para>The pages in SWS are created in object-oriented manner. SWS introduces the concept of <wordasword>component</wordasword> - a reusable and configurable element of HTML code</para></listitem>
21
+ </itemizedlist>
22
+
23
+ <para>I'm a web developer and have used a lot of different web development libraries.
24
+ IMHO WebObjects is superior to all of them - there is so little code you need to write and it's so easy to maintain...
25
+ Reading comp.lang.ruby newsgroup I see a lot of posts complaining about lack of enterprise-level application development libraries.
26
+ Ruby is my language of choice since I first started learning it, so I thought I could learn something by implementing library similar to WebObjects and maybe somebody could benefit from my work.
27
+ After all, this is what OpenSource is all about, isn't it? I know there is a lot of work before SWS could be used in business applications, but I am looking forward for the challenge :)
28
+ </para>
29
+
30
+ </sect1>
31
+
32
+ <sect1 id="introduction-whats-new">
33
+ <title>What's new in this version</title>
34
+
35
+ <itemizedlist>
36
+
37
+ <listitem><para>Implemented parent (^) bindings.</para></listitem>
38
+
39
+ <listitem><para>Hack for favicon.ico to avoid unnecessary requests.</para></listitem>
40
+
41
+ <listitem><para>Much better backtrack handling (still not perfect yet) -
42
+ now a few last components are kept and restored when a request to one
43
+ of them comes by.</para></listitem>
44
+
45
+ <listitem><para>SessionCleaner - old sessions are now removed after
46
+ defined time. No more ever-increasing memory usage.</para></listitem>
47
+
48
+ <listitem><para>Improved handling of action components.</para></listitem>
49
+
50
+ <listitem><para>Created JSMenu component.</para></listitem>
51
+
52
+ <listitem><para>Added some slots to exisiting components.</para></listitem>
53
+
54
+ <listitem><para>Fixed a couple of bugs, two especially nasty - memory leak
55
+ and one in component tree creation on Content nested in
56
+ Content.</para></listitem>
57
+
58
+ </itemizedlist>
59
+ </sect1>
60
+
61
+ <sect1 id="license">
62
+ <title>License</title>
63
+
64
+ <para>SWS is licensed under the Ruby license.</para>
65
+
66
+ <orderedlist>
67
+
68
+ <listitem><para>You may make and give away verbatim copies of the source form of the
69
+ software without restriction, provided that you duplicate all of the
70
+ original copyright notices and associated disclaimers.</para></listitem>
71
+
72
+ <listitem><para>You may modify your copy of the software in any way, provided that
73
+ you do at least ONE of the following:</para>
74
+
75
+ <orderedlist>
76
+ <listitem><para>place your modifications in the Public Domain or otherwise
77
+ make them Freely Available, such as by posting said
78
+ modifications to Usenet or an equivalent medium, or by allowing
79
+ the author to include your modifications in the software.</para></listitem>
80
+
81
+ <listitem><para>use the modified software only within your corporation or
82
+ organization.</para></listitem>
83
+
84
+ <listitem><para>rename any non-standard executables so the names do not conflict
85
+ with standard executables, which must also be provided.</para></listitem>
86
+
87
+ <listitem><para>make other distribution arrangements with the author.</para></listitem>
88
+ </orderedlist>
89
+
90
+ </listitem>
91
+
92
+ <listitem><para>You may distribute the software in object code or executable
93
+ form, provided that you do at least ONE of the following:</para>
94
+
95
+ <orderedlist>
96
+ <listitem><para>distribute the executables and library files of the software,
97
+ together with instructions (in the manual page or equivalent)
98
+ on where to get the original distribution.</para></listitem>
99
+
100
+ <listitem><para>accompany the distribution with the machine-readable source of
101
+ the software.</para></listitem>
102
+
103
+ <listitem><para>give non-standard executables non-standard names, with
104
+ instructions on where to get the original software distribution.</para></listitem>
105
+
106
+ <listitem><para>make other distribution arrangements with the author.</para></listitem>
107
+
108
+ </orderedlist>
109
+ </listitem>
110
+
111
+ <listitem><para>You may modify and include the part of the software into any other
112
+ software (possibly commercial).</para></listitem>
113
+
114
+ <listitem><para>The scripts and library files supplied as input to or produced as
115
+ output from the software do not automatically fall under the
116
+ copyright of the software, but belong to whomever generated them,
117
+ and may be sold commercially, and may be aggregated with this
118
+ software.</para></listitem>
119
+
120
+ <listitem><para>THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
121
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
122
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
123
+ PURPOSE.</para></listitem>
124
+
125
+ </orderedlist>
126
+ </sect1>
127
+
128
+
129
+ </chapter>
130
+
@@ -0,0 +1,35 @@
1
+ <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" [
2
+ <!ENTITY introduction SYSTEM "introduction.docbook">
3
+ <!ENTITY installation SYSTEM "installation.docbook">
4
+ <!ENTITY concepts SYSTEM "concepts.docbook">
5
+ <!ENTITY tutorial SYSTEM "tutorial.docbook">
6
+ <!ENTITY todo SYSTEM "todo.docbook">
7
+ ]>
8
+ <book>
9
+
10
+ <bookinfo>
11
+
12
+ <title>The SWS User Guide</title>
13
+
14
+ <authorgroup>
15
+ <author>
16
+ <firstname>Marek</firstname>
17
+ <surname>Janukowicz</surname>
18
+ </author>
19
+ </authorgroup>
20
+
21
+ <date>17.02.2006</date>
22
+ <releaseinfo>0.4</releaseinfo>
23
+
24
+ <abstract>
25
+ <para>This document describes architecture, concepts and the usage of <application>Starware Web System</application> (<application>SWS</application> for short) - the sophisticated web application development library for <ulink url="http://www.ruby-lang.org">Ruby programming language</ulink>. Current version describes the 0.4 version of SWS.</para>
26
+ </abstract>
27
+ </bookinfo>
28
+
29
+ &introduction;
30
+ &installation;
31
+ &concepts;
32
+ &tutorial;
33
+ &todo;
34
+
35
+ </book>
@@ -0,0 +1,38 @@
1
+ <chapter id="todo">
2
+ <title>Bugs & future versions</title>
3
+
4
+ <sect1 id="todo-bugs">
5
+ <title>Known bugs & limitations</title>
6
+ <itemizedlist>
7
+ <listitem><para>Application must be started from the directory it is in</para></listitem>
8
+ <listitem><para>Multiple selection list components (CheckBoxList and Select) do not work properly when something is selected, submitted, all selections removed and submitted again</para></listitem>
9
+ <listitem><para>RadioButton component isn't of much use (but probably you would rather like to use RadioButtonList)</para></listitem>
10
+ </itemizedlist>
11
+ </sect1>
12
+
13
+ <sect1 id="todo-list">
14
+ <title>TODO list</title>
15
+ <itemizedlist>
16
+ <listitem><para>GUI tools (based on KDevelop and Quanta?)</para></listitem>
17
+ <listitem><para>GET requests</para></listitem>
18
+ <listitem><para>Allow the same instance of component to appear more than once of one page</para></listitem>
19
+ <listitem><para>Keep-alive</para></listitem>
20
+ <listitem><para>Concurrent request handling (probably thread-based, but current Ruby thread implementation sucks :/</para></listitem>
21
+ <listitem><para>Framework dependencies</para></listitem>
22
+ <listitem><para>Keeping session ids in URL</para></listitem>
23
+ <listitem><para>Multipart/mixed type form handling</para></listitem>
24
+ <listitem><para>Access control to resources</para></listitem>
25
+ </itemizedlist>
26
+ </sect1>
27
+
28
+ <sect1 id="final-word">
29
+ <title>Final word</title>
30
+
31
+ <para>Even a good library sucks without proper documentation, so I spent a lot of time trying to write this document and the examples. If you are a developer who was ever forced to wrote any documentation you can imagine how painful it was for me :)
32
+ The documentation is surely lacking at some points, so if you see any room for improvement PLEASE <ulink url="mailto:marek@janukowicz.net">contact me</ulink>.
33
+ I would really love to hear from you with any questions, suggestions, praises or blames you may have. If you like SWS, you can easily help me improving it just by dropping me an email.
34
+ If you don't like it, please let me know so I can stop wasting my time :)</para>
35
+ </sect1>
36
+
37
+ </chapter>
38
+
@@ -0,0 +1,594 @@
1
+ <chapter id="tutorial">
2
+ <title>Tutorial</title>
3
+
4
+ <sect1 id="tutorial-intro">
5
+ <title>Introduction</title>
6
+
7
+ <para>The information given in previous chapters may be a bit scarying, so better let's try to write a simple application to convince ourselves that SWS is in fact easy and friendly :) We will write a trivial addressbook application - it will be by no means complete, but should give an overview of writing SWS applications.</para>
8
+ <para>
9
+ Our application will show a set of address cards. The cards will be stored in a YAML file - a hash of address cards per user. The user will first have to log in (only username, no password) and then he will have his cards presented. He will have choice to add, edit, delete cards and show cards details.
10
+ </para>
11
+
12
+ </sect1>
13
+
14
+
15
+ <sect1 id="tutorial-create-application">
16
+ <title>Create the Addressbook application</title>
17
+
18
+ <sect2 id="tutorial-application-config">
19
+ <title>Application config file</title>
20
+
21
+ <para>
22
+ First, create <replaceable>addressbook</replaceable> directory - this will be the main application directory. Enter it and create application config file named <replaceable>application.yaml</replaceable>.
23
+ The listing below presents the contents of the file - let me explain it a bit. First, we declare components (2 of them) - first one is called <replaceable>LoginPage</replaceable> and will be placed in <replaceable>Login</replaceable> dir, second one - called <replaceable>CardBrowse</replaceable> will be placed in <replaceable>CardBrowse</replaceable> dir.
24
+ Then we load the Core framework, containing standard SWS components (like form elements etc.). Our application is a standalone one, so we set the <replaceable>adaptor_class</replaceable> to <replaceable>SWS::Adaptor::Standalone</replaceable> (in fact, this is the default).
25
+ We will use our custom session class (named Session), so we declare it here. New sessions should start on LoginPage page, so we set <replaceable>default_component_class</replaceable> to LoginPage.
26
+ </para>
27
+
28
+ <para>
29
+ Important note: remember the YAML file should be intended using spaces, not tabs! If you will use tabs (or let your editor use it) you will get parsing error.
30
+ </para>
31
+
32
+ <figure>
33
+ <title>application.yaml file</title>
34
+ <programlisting>
35
+ <![CDATA[
36
+ components:
37
+ LoginPage: Login
38
+ CardBrowse: CardBrowse
39
+ frameworks:
40
+ Core: SYSTEM/sws/Core
41
+ adaptor_class: SWS::Adaptor::Standalone
42
+ session_class: Session
43
+ default_component_class: LoginPage
44
+ ]]>
45
+ </programlisting>
46
+ </figure>
47
+
48
+ </sect2>
49
+
50
+ <sect2 id="tutorial-application-rb-file">
51
+ <title>Application Ruby file</title>
52
+
53
+ <para>Now we will create main application Ruby file. Create file <replaceable>addressbook.rb</replaceable> with the contents presented below.
54
+ Let me explain it a bit - after the obvious <replaceable>require</replaceable> clause we define our custom <replaceable>Application</replaceable> class (we don't need it at this point - <replaceable>SWS::Application</replaceable> would be enough) and custom <replaceable>Session</replaceable> class (we will add some methods and attributes later). Then we create Application instance and start it.
55
+ </para>
56
+
57
+ <figure>
58
+ <title>addressbook.rb file</title>
59
+ <programlisting>
60
+ <![CDATA[
61
+ #!/usr/bin/env ruby
62
+
63
+ require 'sws'
64
+
65
+ class Application < SWS::Application
66
+ end
67
+
68
+ class Session < SWS::Session
69
+ end
70
+
71
+ Application.new.run()
72
+ ]]>
73
+ </programlisting>
74
+ </figure>
75
+
76
+ <para>
77
+ Now we can start the application. Just run "ruby addressbook.rb" and point your WWW browser to <ulink url="http://localhost:1234/">http://localhost:1234/</ulink> (1234 is the default port of SWS applications). We don't have all files necessary to run the application yet, so you'll get an exception page with "RuntimeError: Cannot find component files for component LoginPage" message.
78
+ </para>
79
+
80
+ </sect2>
81
+
82
+ <sect2 id="tutorial-first-component">
83
+ <title>First component</title>
84
+
85
+ <para>
86
+ The time has come to create first component. Create <replaceable>Login</replaceable> directory, create a <replaceable>LoginPage.html</replaceable> file inside and open it in your favorite editor.
87
+ The file contains HTML template for the component. This is our login page, so we'll create a trivial HTML form. All form elements (<replaceable>login_form</replaceable>, <replaceable>login_textfield</replaceable> and <replaceable>login_submit</replaceable>) are SWS components (note the <replaceable>sws</replaceable> attribute).
88
+ </para>
89
+
90
+ <figure>
91
+ <title>LoginPage.html file</title>
92
+ <programlisting>
93
+ <![CDATA[
94
+ <html>
95
+ <head><title>Login page</title></head>
96
+ <body>
97
+ <center><h2>Please, log in</h2></center>
98
+ <form sws="login_form">
99
+ <table align="center">
100
+ <tr><td><input type="text" sws="login_textfield"></td></tr>
101
+ <tr><td><input type="submit" sws="login_submit"></td></tr>
102
+ </table>
103
+ </form>
104
+ </body>
105
+ </html>
106
+ ]]>
107
+ </programlisting>
108
+ </figure>
109
+
110
+ <para>
111
+ Now, create <replaceable>LoginPage.rb</replaceable> file in the same directory. This file contains Ruby class for the component. We need writeable attribute for login name (named - surprise, surprise - <replaceable>login_name</replaceable>) and a method we will call on login attempt (<replaceable>perform_login</replaceable>). The method needs to return a component - for now it returns <replaceable>self</replaceable>, as we do not have other components yet.
112
+ </para>
113
+
114
+ <figure>
115
+ <title>LoginPage.rb file</title>
116
+ <programlisting>
117
+ <![CDATA[
118
+ class LoginPage < SWS::Component
119
+
120
+ attr_accessor :login_name
121
+
122
+ def perform_login ()
123
+
124
+ puts( "Login name entered: #{@login_name}" )
125
+ @login_name = "try again"
126
+ return self
127
+
128
+ end
129
+
130
+ end
131
+ ]]>
132
+ </programlisting>
133
+ </figure>
134
+
135
+ <para>
136
+ The next step is to create <replaceable>LoginPage.sws</replaceable> file. The file contains subcomponents bindings - defines how the component Ruby object attributes map to slots of the components present on the template. We connect <replaceable>login_name</replaceable> attribute to <replaceable>value</replaceable> slot of <replaceable>login_textfield</replaceable> subcomponent, <replaceable>perform_login</replaceable> method to <replaceable>action</replaceable> slot of <replaceable>login_submit</replaceable> subcomponent and use a literal string "Log in" as <replaceable>value</replaceable> of the same subcomponent.
137
+ </para>
138
+
139
+ <figure>
140
+ <title>LoginPage.sws file</title>
141
+ <programlisting>
142
+ <![CDATA[
143
+ login_form:
144
+ _class: SWS::Form
145
+
146
+ login_textfield:
147
+ _class: SWS::TextField
148
+ value: login_name
149
+
150
+ login_submit:
151
+ _class: SWS::SubmitButton
152
+ action: perform_login
153
+ value: "'Log in'"
154
+ ]]>
155
+ </programlisting>
156
+ </figure>
157
+
158
+ <para>
159
+ Now we are ready to run the application again. This time you should be presented a simple login page. Try entering any text into textfield and check the console for messages. Congratulations, you now have a simple, but running, SWS application!.
160
+ </para>
161
+ </sect2>
162
+
163
+ </sect1>
164
+
165
+ <sect1 id="tutorial-logic">
166
+ <title>Business logic</title>
167
+
168
+ <para>
169
+ We will now take care of application business logic. Regular applications usually use a relational database for this purpose, so a O-R mapping library like <ulink url="http://www.starware.one.pl/software/sds/">SDS</ulink> can be very helpful. But for now we'll stick to a simple YAML file. We need several steps to introduce business logic to our application.
170
+ </para>
171
+
172
+ <para>
173
+ First, add reading and/or creating the address cards "database" to application constructor. Also implement a method to access the database. Note we don't need to <replaceable>require</replaceable> YAML, as SWS uses it internally, so it is already loaded. We also create method to save the database abck to the file.
174
+ </para>
175
+
176
+ <figure>
177
+ <title>Additions to Application class - address cards database</title>
178
+ <programlisting>
179
+ <![CDATA[
180
+ class Application < SWS::Application
181
+
182
+ attr_reader :db
183
+
184
+ def initialize ( *args )
185
+
186
+ super( *args )
187
+ if ( File.exist?( "db.yaml" ) )
188
+ @db = YAML.load( File.open( "db.yaml" ) )
189
+ else
190
+ @db = Hash.new
191
+ end
192
+
193
+ end
194
+
195
+
196
+ def save_db ()
197
+ File.open( "db.yaml","w" ) { |file| file.write( @db.to_yaml ) }
198
+ end
199
+
200
+ end
201
+ ]]>
202
+ </programlisting>
203
+ </figure>
204
+
205
+ <para>
206
+ We need to store user name and his address cards in a session object, so that we can easily access it on every page the user access. The <replaceable>entries()</replaceable> method retrieves user entries from database stored in Application instance, or creates an empty table if no entries found. We also implement custom writer for the <replaceable>username</replaceable> attribute, so the entries table is reset when the username is set (this can useful when the user changes, eg. when he logs out and in again).
207
+ </para>
208
+
209
+ <figure>
210
+ <title>Additions to Session class - username and entries</title>
211
+ <programlisting>
212
+
213
+ <![CDATA[
214
+ class Session < SWS::Session
215
+
216
+ attr_reader :username
217
+
218
+ def entries ()
219
+
220
+ if ( @entries )
221
+ return @entries
222
+ else
223
+
224
+ @entries = Application.instance.db[@username]
225
+ unless ( @entries )
226
+ @entries = Array.new
227
+ Application.instance.db[@username] = @entries
228
+ end
229
+ return @entries
230
+
231
+ end
232
+
233
+ end
234
+
235
+
236
+ def username= ( username )
237
+
238
+ @entries = nil
239
+ @username = username
240
+
241
+ end
242
+
243
+
244
+ end
245
+ ]]>
246
+ </programlisting>
247
+ </figure>
248
+
249
+ <para>
250
+ We will also need a custom Struct subclass for keeping address card info. We can just add it to the addressbook.rb file.
251
+ </para>
252
+ <figure>
253
+ <title>AddressCard struct</title>
254
+ <programlisting>
255
+ <![CDATA[
256
+ AddressCard = Struct.new( :first_name, :last_name, :phone, :address )
257
+ ]]>
258
+ </programlisting>
259
+ </figure>
260
+
261
+ <para>
262
+ Now we have all we need to handle business logic for our simple application.
263
+ </para>
264
+ </sect1>
265
+
266
+ <sect1 id="tutorial-login-show-cards">
267
+ <title>Login and show user address cards</title>
268
+
269
+ <sect2 id="tutorial-real-login">
270
+ <title>Real login</title>
271
+ <para>
272
+ We will now change our fake login into a real one. We need some changes to the LoginPage component - we will check if user entered any username. If yes, we will go to the CardBrowse page, otherwise we will present a message.
273
+ </para>
274
+ <para>
275
+ Note we don't have to close the <![CDATA[<SPAN>]]> tag for <replaceable>login_message</replaceable> component, but we do it for the sake of HTML validity.
276
+ </para>
277
+ <figure>
278
+ <title>Enhanced LoginPage.html file</title>
279
+ <programlisting>
280
+ <![CDATA[
281
+ ...
282
+ <body>
283
+ <center><h2><span sws="login_message"></span></h2></center>
284
+ <form sws="login_form">
285
+ ]]>
286
+ </programlisting>
287
+ </figure>
288
+
289
+ <figure>
290
+ <title>Enhanced LoginPage.sws file</title>
291
+ <programlisting>
292
+ <![CDATA[
293
+ [add at the end of file]
294
+
295
+ login_message:
296
+ _class: SWS::String
297
+ value: login_message
298
+ ]]>
299
+ </programlisting>
300
+ </figure>
301
+
302
+ <figure>
303
+ <title>Enhanced LoginPage.rb file</title>
304
+ <programlisting>
305
+ <![CDATA[
306
+ class LoginPage < SWS::Component
307
+
308
+ attr_accessor :login_name
309
+ attr_reader :login_message
310
+
311
+ def perform_login ()
312
+
313
+ unless ( @login_name )
314
+ @login_message = "Please enter a username"
315
+ return self
316
+ else
317
+ return Component.create( "CardBrowse", @request )
318
+ end
319
+
320
+ end
321
+
322
+ end
323
+
324
+ ]]>
325
+ </programlisting>
326
+ </figure>
327
+
328
+ </sect2>
329
+
330
+ <sect2 id="tutorial-card-browse">
331
+ <title>CardBrowse component</title>
332
+
333
+ <para>
334
+ We will now create CardBrowse component (we just created a reference to it in LoginPage.rb).
335
+ This component will display all cards of the logged in user. It will also contain links and buttons to perform basic operations (add,edit,delete) on the cards.
336
+ </para>
337
+
338
+ <para>
339
+ Create CardBrowse directory in main application directory. Create CardBrowse.html file inside - contents of the file is presented below. It a bit complicated, so let's try to clear it out a bit.
340
+ After the common header we see a username and than a table displaying all AddressCards for the user (using a new component - repetition - that renders its contents multiple times, each time using next element from its <replaceable>list</replaceable> slot) - each entry contains first and last name and a couple of buttons - <wordasword>Edit</wordasword> and <wordasword>Add</wordasword>.
341
+ First name is linked and clicking it will show full card details.Next table will appear only if a card was selected for edition or a new one is being added and the last table - only if a first name was clicked.
342
+ </para>
343
+ <para>
344
+ Note the <replaceable>type</replaceable> attributes of <![CDATA[<INPUT>]]> tags are completely optional - SWS ignores them and renders the component depending only of bindings defined in .api file.
345
+ </para>
346
+ <figure>
347
+ <title>CardBrowse.html file</title>
348
+ <programlisting>
349
+ <![CDATA[
350
+ <html>
351
+ <head><title>Address card browsing</title></head>
352
+ <body>
353
+ <center><h2>User <span sws="username"></span></h2></center>
354
+ <form sws="browse_form">
355
+ <table border=1>
356
+ <tr><th>First name</th><th>Last name</th><th>Actions</th></tr>
357
+ <span sws="entries_repetition">
358
+ <tr>
359
+ <td><a sws="show_details_link"><span sws="cursor_first_name_string"></span></a></td>
360
+ <td><span sws="cursor_last_name_string"></span></td>
361
+ <td><input type="submit" sws="edit_card_button"><input type="submit" sws="delete_card_button"></td>
362
+ </tr>
363
+ </span>
364
+ <tr><td colspan=3><input type="submit" sws="add_card_button"></td></tr>
365
+ <tr><td colspan=3><input type="submit" sws="save_changes_button"></td></tr>
366
+ </table>
367
+ </form>
368
+
369
+ <div sws="show_edit_conditional">
370
+ <form sws="edit_form">
371
+ <table border=1>
372
+ <tr><th>First name</th><td><input type="text" sws="edit_first_name_textfield"></td></tr>
373
+ <tr><th>Last name</th><td><input type="text" sws="edit_last_name_textfield"></td></tr>
374
+ <tr><th>Address</th><td><input type="text" sws="edit_address_textfield"></td></tr>
375
+ <tr><th>Phone</th><td><input type="text" sws="edit_phone_textfield"></td></tr>
376
+ <tr><td colspan=2><input type="submit" sws="commit_button"></td></tr>
377
+ </table>
378
+ </form>
379
+ </div>
380
+
381
+ <div sws="show_details_conditional">
382
+ <table border=1>
383
+ <tr><th>First name</th><td><span sws="details_first_name_string"></span></td></tr>
384
+ <tr><th>Last name</th><td><span sws="details_last_name_string"></span></td></tr>
385
+ <tr><th>Address</th><td><span sws="details_address_string"></span></td></tr>
386
+ <tr><th>Phone</th><td><span sws="details_phone_string"></span></td></tr>
387
+ </table>
388
+
389
+ </div>
390
+
391
+ </body>
392
+ </html>
393
+ ]]>
394
+ </programlisting>
395
+ </figure>
396
+
397
+ <para>
398
+ Then create CardBrowse.sws file. There are bindings defined for all components present on this page.
399
+ </para>
400
+
401
+ <figure>
402
+ <title>CardBrowse.sws file</title>
403
+ <programlisting>
404
+ <![CDATA[
405
+ username:
406
+ _class: SWS::String
407
+ value: session.username
408
+
409
+ browse_form:
410
+ _class: SWS::Form
411
+
412
+ entries_repetition:
413
+ _class: SWS::Repetition
414
+ list: session.entries
415
+ item: card_cursor
416
+
417
+ show_details_link:
418
+ _class: SWS::Hyperlink
419
+ action: show_details
420
+
421
+ cursor_first_name_string:
422
+ _class: SWS::String
423
+ value: card_cursor.first_name
424
+
425
+ cursor_last_name_string:
426
+ _class: SWS::String
427
+ value: card_cursor.last_name
428
+
429
+ edit_card_button:
430
+ _class: SWS::SubmitButton
431
+ value: "'Edit'"
432
+ action: edit_card
433
+
434
+ delete_card_button:
435
+ _class: SWS::SubmitButton
436
+ value: "'Delete'"
437
+ action: delete_card
438
+
439
+ add_card_button:
440
+ _class: SWS::SubmitButton
441
+ value: "'Add card'"
442
+ action: add_card
443
+
444
+ save_changes_button:
445
+ _class: SWS::SubmitButton
446
+ value: "'Save changes'"
447
+ action: save_changes
448
+
449
+ show_edit_conditional:
450
+ _class: SWS::Conditional
451
+ condition: edited_card
452
+
453
+ edit_form:
454
+ _class: SWS::Form
455
+
456
+ edit_first_name_textfield:
457
+ _class: SWS::TextField
458
+ value: edited_card.first_name
459
+
460
+ edit_last_name_textfield:
461
+ _class: SWS::TextField
462
+ value: edited_card.last_name
463
+
464
+ edit_address_textfield:
465
+ _class: SWS::TextField
466
+ value: edited_card.address
467
+
468
+ edit_phone_textfield:
469
+ _class: SWS::TextField
470
+ value: edited_card.phone
471
+
472
+ commit_button:
473
+ _class: SWS::SubmitButton
474
+ value: "'Commit'"
475
+ action: commit_card
476
+
477
+ show_details_conditional:
478
+ _class: SWS::Conditional
479
+ condition: selected_card
480
+
481
+ details_first_name_string:
482
+ _class: SWS::String
483
+ value: selected_card.first_name
484
+
485
+ details_last_name_string:
486
+ _class: SWS::String
487
+ value: selected_card.last_name
488
+
489
+ details_address_string:
490
+ _class: SWS::String
491
+ value: selected_card.address
492
+
493
+ details_phone_string:
494
+ _class: SWS::String
495
+ value: selected_card.phone
496
+ ]]>
497
+ </programlisting>
498
+ </figure>
499
+
500
+ <para>
501
+ Next, create the CardBrowse.rb file. It contains a simple CardBrowse class, having a instance variable for repetition cursor, 2 variables for selected and edited card and a bunch of methods to call on button and link clicks.
502
+ </para>
503
+
504
+ <figure>
505
+ <title>CardBrowse.rb file</title>
506
+
507
+ <programlisting>
508
+ <![CDATA[
509
+ class CardBrowse < SWS::Component
510
+
511
+ attr_accessor :card_cursor
512
+ attr_reader :selected_card, :edited_card
513
+
514
+ def delete_card ()
515
+
516
+ session.entries.delete( @card_cursor )
517
+ @selected_card = nil
518
+ @edited_card = nil
519
+
520
+ return self
521
+
522
+ end
523
+
524
+
525
+ def add_card ()
526
+
527
+ @edited_card = AddressCard.new
528
+ session.entries << @edited_card
529
+ @selected_card = nil
530
+
531
+ return self
532
+
533
+ end
534
+
535
+
536
+ def edit_card ()
537
+
538
+ @edited_card = @card_cursor
539
+ @selected_card = nil
540
+
541
+ return self
542
+
543
+ end
544
+
545
+
546
+ def show_details ()
547
+
548
+ @selected_card = @card_cursor
549
+ @edited_card = nil
550
+
551
+ return self
552
+
553
+ end
554
+
555
+
556
+ def commit_card ()
557
+
558
+ @selected_card = nil
559
+ @edited_card = nil
560
+
561
+ return self
562
+
563
+ end
564
+
565
+
566
+ def save_changes ()
567
+
568
+ app.save_db()
569
+ return self
570
+
571
+ end
572
+
573
+ end
574
+ ]]>
575
+ </programlisting>
576
+ </figure>
577
+ </sect2>
578
+
579
+ </sect1>
580
+
581
+ <sect1 id="tutorial-summary">
582
+ <title>Summary</title>
583
+
584
+ <para>
585
+ We have now a simple, but running and complete SWS application. You can now mess around a bit and check if it works correctly.
586
+ </para>
587
+
588
+ <para>
589
+ The code for this tutorial can be found in examples/addressbook directory of SWS source tree.
590
+ </para>
591
+ </sect1>
592
+
593
+ </chapter>
594
+