ape 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +19 -0
  3. data/Manifest +45 -0
  4. data/README +12 -0
  5. data/ape.gemspec +57 -0
  6. data/bin/ape_server +28 -0
  7. data/lib/ape.rb +982 -0
  8. data/lib/ape/atomURI.rb +73 -0
  9. data/lib/ape/auth/google_login_credentials.rb +96 -0
  10. data/lib/ape/auth/wsse_credentials.rb +25 -0
  11. data/lib/ape/authent.rb +42 -0
  12. data/lib/ape/categories.rb +95 -0
  13. data/lib/ape/collection.rb +51 -0
  14. data/lib/ape/crumbs.rb +39 -0
  15. data/lib/ape/entry.rb +151 -0
  16. data/lib/ape/escaper.rb +29 -0
  17. data/lib/ape/feed.rb +117 -0
  18. data/lib/ape/handler.rb +34 -0
  19. data/lib/ape/html.rb +17 -0
  20. data/lib/ape/invoker.rb +54 -0
  21. data/lib/ape/invokers/deleter.rb +31 -0
  22. data/lib/ape/invokers/getter.rb +80 -0
  23. data/lib/ape/invokers/poster.rb +57 -0
  24. data/lib/ape/invokers/putter.rb +46 -0
  25. data/lib/ape/layout/ape.css +56 -0
  26. data/lib/ape/layout/ape_logo.png +0 -0
  27. data/lib/ape/layout/index.html +54 -0
  28. data/lib/ape/layout/info.png +0 -0
  29. data/lib/ape/names.rb +24 -0
  30. data/lib/ape/print_writer.rb +21 -0
  31. data/lib/ape/samples.rb +180 -0
  32. data/lib/ape/samples/atom_schema.txt +338 -0
  33. data/lib/ape/samples/basic_entry.eruby +16 -0
  34. data/lib/ape/samples/categories_schema.txt +69 -0
  35. data/lib/ape/samples/mini_entry.eruby +8 -0
  36. data/lib/ape/samples/service_schema.txt +187 -0
  37. data/lib/ape/samples/unclean_xhtml_entry.eruby +21 -0
  38. data/lib/ape/server.rb +32 -0
  39. data/lib/ape/service.rb +12 -0
  40. data/lib/ape/validator.rb +65 -0
  41. data/lib/ape/version.rb +9 -0
  42. data/scripts/go.rb +29 -0
  43. data/test/test_helper.rb +17 -0
  44. data/test/unit/authent_test.rb +35 -0
  45. data/test/unit/invoker_test.rb +25 -0
  46. data/test/unit/samples_test.rb +36 -0
  47. metadata +111 -0
@@ -0,0 +1,56 @@
1
+ h2 {
2
+ color: #086;
3
+ }
4
+
5
+ body {
6
+ margin-left: 50px;
7
+ margin-right: 100px;
8
+ font-family: verdana, arial, sans-serif;
9
+ }
10
+
11
+ .dialog {
12
+ border-left: 8px solid #eee;
13
+ }
14
+ .from {
15
+ padding: 0 10px 0 10px;
16
+ background: #ddf;
17
+ }
18
+ .to {
19
+ padding: 0 10px 0 10px;
20
+ background: #dfd;
21
+ }
22
+ .dialab {
23
+ padding: 3px 10px 3px 0px;
24
+ background: #eee;
25
+ }
26
+
27
+ a.diaref {
28
+ text-decoration: none;
29
+ font-weight: bold;
30
+ color: #086;
31
+ }
32
+
33
+ span.good {
34
+ font-size: 125%;
35
+ color: green;
36
+ }
37
+ span.warning {
38
+ font-size: 125%;
39
+ font-weight: bold;
40
+ color: #fa0;
41
+ }
42
+ span.error {
43
+ font-size: 125%;
44
+ font-weight: bold;
45
+ color: red;
46
+ }
47
+
48
+ #ape {
49
+ position: absolute; right: 100px; top: 8px;
50
+ padding-left: 20px;
51
+ padding-right: 10px;
52
+
53
+ width: 170px;
54
+ border: 1px solid #086;
55
+ }
56
+
@@ -0,0 +1,54 @@
1
+ <html>
2
+ <head>
3
+ <title>Atom Protocol Exerciser</title>
4
+ <link rel='stylesheet' type='text/css' href='/layout/ape.css' />
5
+ <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
6
+ </head>
7
+ <body>
8
+ <img align="right" src="/ape/ape_logo.png" alt="The Ape logo by Greg Borenstein" />
9
+ <div id="main">
10
+ <h2>Atom Protocol Exerciser</h2>
11
+
12
+ <p>(Only the URI argument is required)</p>
13
+ <form action="/atompub/go" method="post">
14
+ <table>
15
+ <tr>
16
+ <td align="right">URI:</td>
17
+ <td align="left"><input name="uri" type="text" size="55" maxlength="255" /></td>
18
+ </tr>
19
+ <tr>
20
+
21
+ <td align="right">Username:</td>
22
+ <td align="left"><input name="username" type="text" size="55" maxlength="55" /></td>
23
+ </tr>
24
+ <tr>
25
+ <td align="right">Password:</td>
26
+ <td align="left"><input name="password" type="password" /></td>
27
+ </tr>
28
+ <tr>
29
+
30
+ <td></td>
31
+ <td align="left"><input type="submit" value="Exercise!" /></td>
32
+ </tr>
33
+ </table>
34
+ </form>
35
+ </div>
36
+ <!--
37
+ <div id="ape"><p>(It would be nice if there were a cute picture of an ape
38
+ here, ideally grooming another ape for fleas and parasites.
39
+ Got one?)</p></div>
40
+ -->
41
+ <hr/>
42
+ <p>The Ape source code may be found at
43
+ <a href="http://ape.dev.java.net">ape.dev.java.net</a>. It's a little
44
+ tricky to get going;
45
+ until I fix that, see this
46
+ <a href="http://rollerweblogger.org/roller/entry/atom_protocol_exerciser_ape_setup">discussion by Dave Johnson</a>.</p>
47
+
48
+ <hr/>
49
+ <p>Ape Logo by <a href="http://www.urbanhonking.com/ideasfordozens">Greg
50
+ Borenstein</a>.</p>
51
+ </div>
52
+ </body>
53
+ </html>
54
+
Binary file
@@ -0,0 +1,24 @@
1
+ # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
2
+ # Use is subject to license terms - see file "LICENSE"
3
+
4
+ module Ape
5
+ module Names
6
+ AtomNamespace = 'http://www.w3.org/2005/Atom' unless defined?(AtomNamespace)
7
+ AppNamespace = 'http://www.w3.org/2007/app' unless defined?(AppNamespace)
8
+ DcNamespace = 'http://purl.org/dc/elements/1.1/' unless defined?(DcNamespace)
9
+ XhtmlNamespace = 'http://www.w3.org/1999/xhtml' unless defined?(XhtmlNamespace)
10
+ XmlNamespaces = {
11
+ 'app' => AppNamespace,
12
+ 'atom' => AtomNamespace,
13
+ 'dc' => DcNamespace,
14
+ 'xhtml' => XhtmlNamespace
15
+ } unless defined?(XmlNamespaces)
16
+
17
+ AtomMediaType = 'application/atom+xml' unless defined?(AtomMediaType)
18
+ AtomEntryMediaType = 'application/atom+xml;type=entry' unless defined?(AtomEntryMediaType)
19
+ AppMediaType = 'application/atomsvc+xml' unless defined?(AppMediaType)
20
+
21
+ end
22
+ end
23
+
24
+
@@ -0,0 +1,21 @@
1
+ # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
2
+ # Use is subject to license terms - see file "LICENSE"
3
+
4
+ # this is a wrapper for the weird derived-from-PrintWriter class that comes
5
+ # out of HttpResponse.getWriter
6
+
7
+ module Ape
8
+ class Printwriter
9
+ def initialize(java_writer)
10
+ @w = java_writer
11
+ end
12
+
13
+ def puts(s)
14
+ @w.println s
15
+ end
16
+
17
+ def << (s)
18
+ @w.print s
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,180 @@
1
+ # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
2
+ # Use is subject to license terms - see file "LICENSE"
3
+ require 'rexml/xpath'
4
+ require 'date'
5
+ require 'base64'
6
+ require 'erubis'
7
+
8
+ module Ape
9
+ class Samples
10
+
11
+ @@service_schema = nil
12
+ @@categories_schema = nil
13
+ @@atom_schema = nil
14
+ @@home = nil
15
+
16
+ def Samples.home=(home)
17
+ @@home = home
18
+ end
19
+
20
+ def Samples.foreign_child
21
+ 'subject'
22
+ end
23
+ def Samples.foreign_namespace
24
+ Names::DcNamespace
25
+ end
26
+ def Samples.foreign_child_content
27
+ 'Simians'
28
+ end
29
+
30
+ def Samples.load_schema(file_name)
31
+ schema = ""
32
+ File.open(File.join(File.dirname(__FILE__), "/samples/#{file_name}_schema.txt")) do |file|
33
+ while(line = file.gets)
34
+ schema << line
35
+ end
36
+ end
37
+ schema
38
+ end
39
+
40
+ def Samples.service_RNC
41
+ @@service_schema = load_schema('service') unless @@service_schema
42
+ @@service_schema
43
+ end
44
+
45
+ def Samples.categories_RNC
46
+ @@categories_schema = load_schema('categories') unless @@categories_schema
47
+ @@categories_schema
48
+ end
49
+
50
+ def Samples.atom_RNC
51
+ @@atom_schema = load_schema('atom') unless @@atom_schema
52
+ @@atom_schema
53
+ end
54
+
55
+ #recipe from cap
56
+ def Samples.home_directory
57
+ ENV["HOME"] || (ENV["HOMEPATH"] && "#{ENV["HOMEDRIVE"]}#{ENV["HOMEPATH"]}") ||
58
+ "/"
59
+ end
60
+
61
+ def Samples.home
62
+ @@home || ENV["APE_HOME"] || File.join(home_directory,".ape")
63
+ end
64
+
65
+ def Samples.make_id
66
+ id = ''
67
+ 5.times { id += rand(1000000).to_s }
68
+ "tag:tbray.org,2005:#{id}"
69
+ end
70
+
71
+ def Samples.entry_path(type)
72
+ File.exist?(File.join(home, "/#{type}.eruby"))?
73
+ File.join(home, "/#{type}.eruby") :
74
+ File.join(File.dirname(__FILE__), "/samples/#{type}.eruby")
75
+ end
76
+
77
+ def Samples.load_template(type)
78
+ entry_path = entry_path(type)
79
+ input = File.read(entry_path)
80
+ eruby = Erubis::Eruby.new(input)
81
+ end
82
+
83
+
84
+ def Samples.mini_entry
85
+ eruby = load_template('mini_entry')
86
+
87
+ now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
88
+ id = make_id
89
+
90
+ eruby.result(binding())
91
+ end
92
+
93
+ def Samples.basic_entry
94
+ eruby = load_template('basic_entry')
95
+
96
+ id = make_id
97
+ now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
98
+ title = Escaper.escape('From the <APE> (サル)')
99
+ summary = "Summary from the &lt;b>&amp;lt;&amp;nbsp;APE&amp;nbsp;>&lt;/b> at #{now}"
100
+ subject = Names::DcNamespace
101
+
102
+ eruby.result(binding())
103
+ end
104
+
105
+ def Samples.unclean_xhtml_entry
106
+ eruby = load_template('unclean_xhtml_entry')
107
+
108
+ id = make_id
109
+ now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
110
+
111
+ eruby.result(binding())
112
+ end
113
+
114
+ def Samples.cat_test_entry
115
+ e = retitled_entry('Testing category posting')
116
+ end
117
+
118
+ def Samples.retitled_entry(new_title, new_id = nil)
119
+ e = basic_entry
120
+ e.gsub!(/<title>.*<\/title>/, "<title>#{new_title}</title>")
121
+ new_id = make_id unless new_id
122
+ e.gsub(/<id>.*<\/id>/, "<id>#{new_id}</id>")
123
+ end
124
+
125
+ def Samples.picture
126
+ b64 =<<END_OF_PICTURE
127
+ /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQE
128
+ BQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/
129
+ 2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU
130
+ FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAA8AEYDASIAAhEBAxEB/8QA
131
+ HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF
132
+ BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK
133
+ FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1
134
+ dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG
135
+ x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB
136
+ AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC
137
+ AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom
138
+ JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE
139
+ hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU
140
+ 1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5fgS0knaT
141
+ 7C4UddzHINab3lrboz/2ezop5LAnj3qtbK1xJbwbcZYZwc4GeprpZLNZY5Tu
142
+ Ai8skhjgHk5FfJNtWVm/m/8Agn7M6dOGzSdvzdvKxl2VzaAec2n7uMqDnBHr
143
+ W1FPEY7YDTrVYXUNl5fw/wAf0qjps0NzbCEMUZMBhnJz9PwqzZbISIpQj27s
144
+ A+4ZK99w/wAKycU229fm/wDM9aVDlhF09uv9eRrG6jmgeGLTrR2kPG2T5uMe
145
+ /wDnNcnrGuxpdGMWCM4cR7V+bkehzXSatJBo4S3sZFkmkQ75x0QZ7H1965z7
146
+ MjIxDsAEJyTkgY5b9KjlitV+N/yuXSoyrO/T835FKe/Ms8khsMFRtIwR2780
147
+ 77bGqQObHZORwgz8wHGRz6/yqOxkZmnO1isjYXeMZB4yPyqvrSJa30DF2bdu
148
+ 4TtycZq7Nbp2stdTgapyqciaerTXp899uhFc6qLmQg23X5iTxz9aKZes0RRX
149
+ yxA+7n9aK6YcvKrq/wAzkr4W1R8rsu1rnSWD2UcLPIgE3BIDHJPt7ZqBXmiv
150
+ ZnuSTaMCpUAHy89P8j1pdKtGe7jllI+i84H9TW7JIt/PLYCGREyfKbachu5P
151
+ P/6q578rf3/1/X/B6mkopTV2/wAv6X4fNRp4ZitLQXsLZlUL8p5LAjg/XFVz
152
+ fRKky+YMbCd7L19himadPLat9lD5jALKqtyuPT2961bfT4LlELQA+ZuyVXJz
153
+ nrWMlKKbk9j0KOIVLlp2321f/D9bmH9uihxNJgKNqnjJxjJx+NFuh1RONjRE
154
+ gkgYIHOFH+eK0tV0m3adDFEjgEbuM5yAc5z+lVpA+nRGOMrGGOPNPAXHO0de
155
+ abkqvNKP9alLETgoULJfPy+X6kGqWovJRaQqqxx4DzoM9Ow9ag8iNmeN4wXG
156
+ V3tgluevsa0WlSw0uS5hj82RW2kOp2ITzuPHX6etQ3CJcWkdyymJpGLbTxsI
157
+ /iXjp9aiOiv8vnv+Wv8AWtT9m6iw6Wq6+d7Wt2vp6+W3NyqtlIEm6c7XKZ9O
158
+ MGirjz/aUDOFkOfvOOG9+aK9OnhnUipXt8m/0PCq5p7Cbpyim115kv1ROzSy
159
+ SR3UK5jx8yg8tzXW3Gtw3Gm26QQBbtkCO6DmQjp2yP61yGk34SQIGco5ypHY
160
+ 9s1q2MS2l1LdKCNg3le3ORj8a5uTbmt5f1/XT5ae1UVZpvr3/r0/4ZpNp76X
161
+ Os2AZW6KTgZJ6Y9OKgOszpAyu6IEPAKA5IPT2qxFeG/Xz5mbzpAQikFsL6/l
162
+ WXcp5832e2KuZPmKsCNo9/50KN07nalCmvaTev8AwxqrqNxe2kzI0RkGAFMa
163
+ 7j1PXHHpVqKZLm3WK+aOUld5IbAb6+9Zl5af2WqnzQ1lN0lQcbsY4PXr61Qa
164
+ 5JixvIJbCgDGCO9KUFq1szWm4VaSS0kvw/H8jcivItPlSDasqEfJuAAYd1PH
165
+ A9aoavOt3O4wTC5y74+97cDoMdRUEc39p2brONoiOHOcYb1pmrShII7W2Oc8
166
+ LzyB2rPka1fb+v8AP8fSXiVN8qXv7X8lv/lfsmvWhqBF5MiWsqQhF+ZiePoK
167
+ KpzfuI1C/OqkqV2d/WiuqMqkFaLsjklh8K3erFN9/wCmSaNqBSPdNE5KkMML
168
+ 1561tJrqSSylbaQh49oXgdjyea5S21W5ReJPb8KvW2sXQlzvByhPI+tdUqDb
169
+ TaX3v/I+bjjHy2v+C/UmfWjazbYopHToQeorSj1WGGzkRSwuZT+8kdSSB/d6
170
+ dKw/t0skr7tp2kAfKKsapqs4kiAKjChshRnOBS9k7ar8f+AavFyqa30Xl/wT
171
+ RjvtkDW8zs9q+NyMh+96ism4umR9kSSSgEqrn+Jc/pSf23ds0zFwdvIBUYzV
172
+ aLU55DG5Khj3CipdKTbdlr5/8A1hifZ2af8AX3luDUC0dxCySoJG3byBj8fy
173
+ qG71KW4nSRomwq7CV4OO2KonVLgblDAAcDAqAanO8+0kADngURoXbdvxZM8Y
174
+ 001+n/Dk41B8ZaFmx8oyx6UVVn1m5jRCGXOMfdorRU2lqvx/4BhPFzlK6lb5
175
+ I//Z
176
+ END_OF_PICTURE
177
+ Base64.decode64(b64)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,338 @@
1
+ # -*- rnc -*-
2
+ # RELAX NG Compact Syntax Grammar for the
3
+ # Atom Format Specification Version 11
4
+
5
+ namespace atom = "http://www.w3.org/2005/Atom"
6
+ namespace xhtml = "http://www.w3.org/1999/xhtml"
7
+ namespace s = "http://www.ascc.net/xml/schematron"
8
+ namespace local = ""
9
+
10
+ start = atomFeed | atomEntry
11
+
12
+ # Common attributes
13
+
14
+ atomCommonAttributes =
15
+ attribute xml:base { atomUri }?,
16
+ attribute xml:lang { atomLanguageTag }?,
17
+ undefinedAttribute*
18
+
19
+ # Text Constructs
20
+
21
+ atomPlainTextConstruct =
22
+ atomCommonAttributes,
23
+ attribute type { "text" | "html" }?,
24
+ text
25
+
26
+ atomXHTMLTextConstruct =
27
+ atomCommonAttributes,
28
+ attribute type { "xhtml" },
29
+ xhtmlDiv
30
+
31
+ atomTextConstruct = atomPlainTextConstruct | atomXHTMLTextConstruct
32
+
33
+ # Person Construct
34
+
35
+ atomPersonConstruct =
36
+ atomCommonAttributes,
37
+ (element atom:name { text }
38
+ & element atom:uri { atomUri }?
39
+ & element atom:email { atomEmailAddress }?
40
+ & extensionElement*)
41
+
42
+ # Date Construct
43
+
44
+ atomDateConstruct =
45
+ atomCommonAttributes,
46
+ xsd:dateTime
47
+
48
+ # atom:feed
49
+
50
+ atomFeed =
51
+ [
52
+ s:rule [
53
+ context = "atom:feed"
54
+ s:assert [
55
+ test = "atom:author or not(atom:entry[not(atom:author)])"
56
+ "An atom:feed must have an atom:author unless all "
57
+ ~ "of its atom:entry children have an atom:author."
58
+ ]
59
+ ]
60
+ ]
61
+ element atom:feed {
62
+ atomCommonAttributes,
63
+ (atomAuthor*
64
+ & atomCategory*
65
+ & atomContributor*
66
+ & atomGenerator?
67
+ & atomIcon?
68
+ & atomId
69
+ & atomLink*
70
+ & atomLogo?
71
+ & atomRights?
72
+ & atomSubtitle?
73
+ & atomTitle
74
+ & atomUpdated
75
+ & extensionElement*),
76
+ atomEntry*
77
+ }
78
+
79
+ # atom:entry
80
+
81
+ atomEntry =
82
+ [
83
+ s:rule [
84
+ context = "atom:entry"
85
+ s:assert [
86
+ test = "atom:link[@rel='alternate'] "
87
+ ~ "or atom:link[not(@rel)] "
88
+ ~ "or atom:content"
89
+ "An atom:entry must have at least one atom:link element "
90
+ ~ "with a rel attribute of 'alternate' "
91
+ ~ "or an atom:content."
92
+ ]
93
+ ]
94
+ s:rule [
95
+ context = "atom:entry"
96
+ s:assert [
97
+ test = "atom:author or "
98
+ ~ "../atom:author or atom:source/atom:author"
99
+ "An atom:entry must have an atom:author "
100
+ ~ "if its feed does not."
101
+ ]
102
+ ]
103
+ ]
104
+ element atom:entry {
105
+ atomCommonAttributes,
106
+ (atomAuthor*
107
+ & atomCategory*
108
+ & atomContent?
109
+ & atomContributor*
110
+ & atomId
111
+ & atomLink*
112
+ & atomPublished?
113
+ & atomRights?
114
+ & atomSource?
115
+ & atomSummary?
116
+ & atomTitle
117
+ & atomUpdated
118
+ & extensionElement*)
119
+ }
120
+
121
+ # atom:content
122
+
123
+ atomInlineTextContent =
124
+ element atom:content {
125
+ atomCommonAttributes,
126
+ attribute type { "text" | "html" }?,
127
+ (text)*
128
+ }
129
+
130
+ atomInlineXHTMLContent =
131
+ element atom:content {
132
+ atomCommonAttributes,
133
+ attribute type { "xhtml" },
134
+ xhtmlDiv
135
+ }
136
+
137
+ atomInlineOtherContent =
138
+ element atom:content {
139
+ atomCommonAttributes,
140
+ attribute type { atomMediaType }?,
141
+ (text|anyElement)*
142
+ }
143
+
144
+ atomOutOfLineContent =
145
+ element atom:content {
146
+ atomCommonAttributes,
147
+ attribute type { atomMediaType }?,
148
+ attribute src { atomUri },
149
+ empty
150
+ }
151
+
152
+ atomContent = atomInlineTextContent
153
+ | atomInlineXHTMLContent
154
+ | atomInlineOtherContent
155
+ | atomOutOfLineContent
156
+
157
+ # atom:author
158
+
159
+ atomAuthor = element atom:author { atomPersonConstruct }
160
+
161
+ # atom:category
162
+
163
+ atomCategory =
164
+ element atom:category {
165
+ atomCommonAttributes,
166
+ attribute term { text },
167
+ attribute scheme { atomUri }?,
168
+ attribute label { text }?,
169
+ undefinedContent
170
+ }
171
+
172
+ # atom:contributor
173
+
174
+ atomContributor = element atom:contributor { atomPersonConstruct }
175
+
176
+ # atom:generator
177
+
178
+ atomGenerator = element atom:generator {
179
+ atomCommonAttributes,
180
+ attribute uri { atomUri }?,
181
+ attribute version { text }?,
182
+ text
183
+ }
184
+
185
+ # atom:icon
186
+
187
+ atomIcon = element atom:icon {
188
+ atomCommonAttributes,
189
+ (atomUri)
190
+ }
191
+
192
+ # atom:id
193
+
194
+ atomId = element atom:id {
195
+ atomCommonAttributes,
196
+ (atomUri)
197
+ }
198
+
199
+ # atom:logo
200
+
201
+ atomLogo = element atom:logo {
202
+ atomCommonAttributes,
203
+ (atomUri)
204
+ }
205
+
206
+ # atom:link
207
+
208
+ atomLink =
209
+ element atom:link {
210
+ atomCommonAttributes,
211
+ attribute href { atomUri },
212
+ attribute rel { atomNCName | atomUri }?,
213
+ attribute type { atomMediaType }?,
214
+ attribute hreflang { atomLanguageTag }?,
215
+ attribute title { text }?,
216
+ attribute length { text }?,
217
+ undefinedContent
218
+ }
219
+
220
+ # atom:published
221
+
222
+ atomPublished = element atom:published { atomDateConstruct }
223
+
224
+ # atom:rights
225
+
226
+ atomRights = element atom:rights { atomTextConstruct }
227
+
228
+ # atom:source
229
+
230
+ atomSource =
231
+ element atom:source {
232
+ atomCommonAttributes,
233
+ (atomAuthor*
234
+ & atomCategory*
235
+ & atomContributor*
236
+ & atomGenerator?
237
+ & atomIcon?
238
+ & atomId?
239
+ & atomLink*
240
+ & atomLogo?
241
+ & atomRights?
242
+ & atomSubtitle?
243
+ & atomTitle?
244
+ & atomUpdated?
245
+ & extensionElement*)
246
+ }
247
+
248
+ # atom:subtitle
249
+
250
+ atomSubtitle = element atom:subtitle { atomTextConstruct }
251
+
252
+ # atom:summary
253
+
254
+ atomSummary = element atom:summary { atomTextConstruct }
255
+
256
+ # atom:title
257
+
258
+ atomTitle = element atom:title { atomTextConstruct }
259
+
260
+ # atom:updated
261
+
262
+ atomUpdated = element atom:updated { atomDateConstruct }
263
+
264
+ # Low-level simple types
265
+
266
+ atomNCName = xsd:string { minLength = "1" pattern = "[^:]*" }
267
+
268
+ # Whatever a media type is, it contains at least one slash
269
+ atomMediaType = xsd:string { pattern = ".+/.+" }
270
+
271
+ # As defined in RFC 3066
272
+ atomLanguageTag = xsd:string {
273
+ pattern = "[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*"
274
+ }
275
+
276
+ # Unconstrained; it's not entirely clear how IRI fit into
277
+ # xsd:anyURI so let's not try to constrain it here
278
+ atomUri = text
279
+
280
+ # Whatever an email address is, it contains at least one @
281
+ atomEmailAddress = xsd:string { pattern = ".+@.+" }
282
+
283
+ # Simple Extension
284
+
285
+ simpleExtensionElement =
286
+ element * - atom:* {
287
+ text
288
+ }
289
+
290
+ # Structured Extension
291
+
292
+ structuredExtensionElement =
293
+ element * - atom:* {
294
+ (attribute * { text }+,
295
+ (text|anyElement)*)
296
+ | (attribute * { text }*,
297
+ (text?, anyElement+, (text|anyElement)*))
298
+ }
299
+
300
+ # Other Extensibility
301
+
302
+ extensionElement =
303
+ simpleExtensionElement | structuredExtensionElement
304
+
305
+ undefinedAttribute =
306
+ attribute * - (xml:base | xml:lang | local:*) { text }
307
+
308
+ undefinedContent = (text|anyForeignElement)*
309
+
310
+ anyElement =
311
+ element * {
312
+ (attribute * { text }
313
+ | text
314
+ | anyElement)*
315
+ }
316
+
317
+ anyForeignElement =
318
+ element * - atom:* {
319
+ (attribute * { text }
320
+ | text
321
+ | anyElement)*
322
+ }
323
+
324
+ # XHTML
325
+
326
+ anyXHTML = element xhtml:* {
327
+ (attribute * { text }
328
+ | text
329
+ | anyXHTML)*
330
+ }
331
+
332
+ xhtmlDiv = element xhtml:div {
333
+ (attribute * { text }
334
+ | text
335
+ | anyXHTML)*
336
+ }
337
+
338
+ # EOF