riddl 0.99.105

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 (92) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +165 -0
  3. data/INSTALL +24 -0
  4. data/README.rdoc +2 -0
  5. data/Rakefile +17 -0
  6. data/TODO +17 -0
  7. data/contrib/riddl.jpg +0 -0
  8. data/contrib/riddl.png +0 -0
  9. data/contrib/riddl.svg +138 -0
  10. data/lib/riddl/client.rb +423 -0
  11. data/lib/riddl/commonlogger.rb +16 -0
  12. data/lib/riddl/constants.rb +5 -0
  13. data/lib/riddl/error.rb +8 -0
  14. data/lib/riddl/handlers.rb +14 -0
  15. data/lib/riddl/handlers/oauth.rb +19 -0
  16. data/lib/riddl/handlers/plain-type.rb +21 -0
  17. data/lib/riddl/handlers/relaxng.rb +16 -0
  18. data/lib/riddl/handlers/xmlschema.rb +16 -0
  19. data/lib/riddl/header.rb +9 -0
  20. data/lib/riddl/implementation.rb +57 -0
  21. data/lib/riddl/ns/common-patterns/addon-security/request.xml +25 -0
  22. data/lib/riddl/ns/common-patterns/addon-security/response.xml +25 -0
  23. data/lib/riddl/ns/common-patterns/downloadify/1.0/downloadify.xml +18 -0
  24. data/lib/riddl/ns/common-patterns/notifications-consumer/1.0/consumer.xml +100 -0
  25. data/lib/riddl/ns/common-patterns/notifications-producer/1.0/producer.xml +204 -0
  26. data/lib/riddl/ns/common-patterns/properties/1.0/properties.schema.schema +140 -0
  27. data/lib/riddl/ns/common-patterns/properties/1.0/properties.schema.xsl +89 -0
  28. data/lib/riddl/ns/common-patterns/properties/1.0/properties.xml +150 -0
  29. data/lib/riddl/ns/common/datatypes-1_0.rng +79 -0
  30. data/lib/riddl/ns/common/relaxng-modular.rng +330 -0
  31. data/lib/riddl/ns/common/relaxng.rng +10 -0
  32. data/lib/riddl/ns/declaration/1.0/declaration.rng +114 -0
  33. data/lib/riddl/ns/description/1.0/description.rng +302 -0
  34. data/lib/riddl/option.rb +9 -0
  35. data/lib/riddl/parameter.rb +54 -0
  36. data/lib/riddl/protocols/http/generator.rb +121 -0
  37. data/lib/riddl/protocols/http/parser.rb +199 -0
  38. data/lib/riddl/protocols/websocket.rb +103 -0
  39. data/lib/riddl/protocols/xmpp/generator.rb +176 -0
  40. data/lib/riddl/protocols/xmpp/parser.rb +118 -0
  41. data/lib/riddl/roles.rb +15 -0
  42. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0%2Faccess_token.rb +30 -0
  43. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0%2Fon_behalf.rb +22 -0
  44. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0%2Frequest_token.rb +30 -0
  45. data/lib/riddl/roles/http%3A%2F%2Foauth.net%2F1.0/base.rb +67 -0
  46. data/lib/riddl/server.rb +519 -0
  47. data/lib/riddl/utils/description.rb +29 -0
  48. data/lib/riddl/utils/downloadify.rb +14 -0
  49. data/lib/riddl/utils/erbserve.rb +23 -0
  50. data/lib/riddl/utils/fileserve.rb +31 -0
  51. data/lib/riddl/utils/notifications_producer.rb +310 -0
  52. data/lib/riddl/utils/properties.rb +474 -0
  53. data/lib/riddl/utils/xsloverlay.rb +21 -0
  54. data/lib/riddl/wrapper.rb +280 -0
  55. data/lib/riddl/wrapper/declaration.rb +103 -0
  56. data/lib/riddl/wrapper/declaration/facade.rb +94 -0
  57. data/lib/riddl/wrapper/declaration/interface.rb +34 -0
  58. data/lib/riddl/wrapper/declaration/tile.rb +107 -0
  59. data/lib/riddl/wrapper/description.rb +69 -0
  60. data/lib/riddl/wrapper/description/access.rb +108 -0
  61. data/lib/riddl/wrapper/description/message_and_transformation.rb +131 -0
  62. data/lib/riddl/wrapper/description/resource.rb +271 -0
  63. data/lib/riddl/wrapper/layerchecker.rb +33 -0
  64. data/lib/riddl/wrapper/messageparser.rb +221 -0
  65. data/lib/riddl/wrapper/resourcechecker.rb +98 -0
  66. data/ns/common-patterns/addon-security/request.xml +25 -0
  67. data/ns/common-patterns/addon-security/response.xml +25 -0
  68. data/ns/common-patterns/downloadify/1.0/downloadify.xml +18 -0
  69. data/ns/common-patterns/notifications-consumer/1.0/consumer.xml +100 -0
  70. data/ns/common-patterns/notifications-producer/1.0/producer.xml +204 -0
  71. data/ns/common-patterns/properties/1.0/properties.schema.schema +140 -0
  72. data/ns/common-patterns/properties/1.0/properties.schema.xsl +89 -0
  73. data/ns/common-patterns/properties/1.0/properties.xml +150 -0
  74. data/ns/common/datatypes-1_0.rng +79 -0
  75. data/ns/common/relaxng-modular.rng +330 -0
  76. data/ns/common/relaxng.rng +10 -0
  77. data/ns/declaration/1.0/declaration.rng +114 -0
  78. data/ns/description/1.0/description.rng +302 -0
  79. data/riddl.gemspec +33 -0
  80. data/test/smartrunner.rb +48 -0
  81. data/test/tc_declaration-distributed.rb +79 -0
  82. data/test/tc_declaration-hybrid.rb +71 -0
  83. data/test/tc_declaration-local.rb +47 -0
  84. data/test/tc_helloworld.rb +17 -0
  85. data/test/tc_producer.rb +54 -0
  86. data/test/tc_properties.rb +72 -0
  87. data/tools/flash-policy-server.rb +12 -0
  88. data/tools/riddlcheck +36 -0
  89. data/tools/riddlcheck-1_0 +36 -0
  90. data/tools/riddlprocess +51 -0
  91. data/tools/riddlprocess-1_0 +51 -0
  92. metadata +291 -0
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../client')
2
+
3
+ module Riddl
4
+ module Utils
5
+ module Description
6
+
7
+ class XML < Riddl::Implementation
8
+ def response
9
+ return Riddl::Parameter::Complex.new("riddl-description","text/xml",@a[0])
10
+ end
11
+ end
12
+
13
+ class Call < Riddl::Implementation
14
+ def response
15
+ client = Riddl::Client.new(@a[3],@a[4])
16
+
17
+ path = client.resource "/" + @a[5]
18
+ @status, result = if @a[0].nil?
19
+ path.request @m => [ Riddl::Header.new("RIDDL_DECLARATION_RESOURCE", @a[2]), Riddl::Header.new("RIDDL-DECLARATION-PATH", @a[1]) ] + @h.map{|a,b| Riddl::Header.new(a,b)} + @p
20
+ else
21
+ path.request @m => [ Riddl::Header.new("RIDDL_DECLARATION_RESOURCE", @a[2]), Riddl::Header.new("RIDDL-DECLARATION-PATH", @a[1]) ] + @a[0].headers.map{|a,b| Riddl::Header.new(a,b)} + @a[0].response
22
+ end
23
+ result
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ require 'mime/types'
2
+ require 'digest/md5'
3
+
4
+ module Riddl
5
+ module Utils
6
+ class Downloadify < Riddl::Implementation
7
+ def response
8
+ mimetype = @p.find{|e|e.name == 'mimetype'}.value
9
+ content = @p.find{|e|e.name == 'content'}.value
10
+ Riddl::Parameter::Complex.new("content",mimetype,content,@r.last)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'mime/types'
2
+ require 'erb'
3
+
4
+ module Riddl
5
+ module Utils
6
+ class ERBServe < Riddl::Implementation
7
+ def response
8
+ path = File.file?(@a[0]) ? @a[0] : "#{@a[0]}/#{@r[@match.length..-1].join('/')}".gsub(/\/+/,'/')
9
+ if File.directory?(path)
10
+ @status = 404
11
+ return []
12
+ end
13
+ if File.exists?(path)
14
+ __ERB_FILE__ = path
15
+ rval = ERB.new(File.read(path), 0, "%<>")
16
+ return Riddl::Parameter::Complex.new("data",MIME::Types.type_for(path).to_s,rval.result(binding))
17
+ end
18
+ @status = 404
19
+ []
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ require 'mime/types'
2
+ require 'digest/md5'
3
+
4
+ module Riddl
5
+ module Utils
6
+ class FileServe < Riddl::Implementation
7
+ def response
8
+ path = File.file?(@a[0]) ? @a[0] : "#{@a[0]}/#{@r[@match.length-1..-1].join('/')}".gsub(/\/+/,'/')
9
+
10
+ if File.directory?(path)
11
+ @status = 404
12
+ return []
13
+ end
14
+ if File.exists?(path)
15
+ mtime = File.mtime(path)
16
+ @headers << Riddl::Header.new("Last-Modified",mtime.httpdate)
17
+ @headers << Riddl::Header.new("ETag",Digest::MD5.hexdigest(mtime.httpdate))
18
+ htime = @env["HTTP_IF_MODIFIED_SINCE"].nil? ? Time.at(0) : Time.parse(@env["HTTP_IF_MODIFIED_SINCE"])
19
+ if htime == mtime
20
+ @headers << Riddl::Header.new("Connection","close")
21
+ @status = 304 # Not modified
22
+ return []
23
+ else
24
+ return Riddl::Parameter::Complex.new("file",MIME::Types.type_for(path).first.to_s,File.open(path,'r'))
25
+ end
26
+ end
27
+ @status = 404
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,310 @@
1
+ module Riddl
2
+ module Utils
3
+ module Notifications
4
+
5
+ module Producer
6
+
7
+ def self::implementation(backend,handler=nil,details=:production)
8
+ unless handler.nil? || (handler.is_a? Riddl::Utils::Notifications::Producer::HandlerBase)
9
+ raise "handler not a subclass of HandlerBase"
10
+ end
11
+ Proc.new do
12
+ on resource "notifications" do
13
+ run Riddl::Utils::Notifications::Producer::Overview if get
14
+ on resource "topics" do
15
+ run Riddl::Utils::Notifications::Producer::Topics, backend if get
16
+ end
17
+ on resource "subscriptions" do
18
+ run Riddl::Utils::Notifications::Producer::Subscriptions, backend, details if get
19
+ run Riddl::Utils::Notifications::Producer::CreateSubscription, backend, handler if post 'subscribe'
20
+ on resource do
21
+ run Riddl::Utils::Notifications::Producer::Subscription, backend, details if get 'request'
22
+ run Riddl::Utils::Notifications::Producer::UpdateSubscription, backend, handler if put 'details'
23
+ run Riddl::Utils::Notifications::Producer::DeleteSubscription, backend, handler if delete 'delete'
24
+ on resource 'ws' do
25
+ run Riddl::Utils::Notifications::Producer::WS, backend, handler if websocket
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ class HandlerBase #{{{
34
+ def initialize(data)
35
+ @data = data
36
+ @key = nil
37
+ @topics = []
38
+ end
39
+ def key(k)
40
+ @key = k
41
+ self
42
+ end
43
+ def topics(t)
44
+ @topics = t
45
+ self
46
+ end
47
+ def ws_open(socket); end
48
+ def ws_close; end
49
+ def ws_message(socket,data); end
50
+ def create; end
51
+ def delete; end
52
+ def update; end
53
+ end #}}}
54
+
55
+ class Backend #{{{
56
+ attr_reader :topics
57
+
58
+ class Sub #{{{
59
+ def initialize(name)
60
+ @name = name
61
+ end
62
+ def modify(&block)
63
+ XML::Smart.modify(@name,"<subscription xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'/>") do |doc|
64
+ doc.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
65
+ block.call doc
66
+ end
67
+ end
68
+ def delete
69
+ FileUtils::rm_rf(File.dirname(@name))
70
+ end
71
+ def to_s
72
+ File.read(@name)
73
+ end
74
+ def view(&block)
75
+ XML::Smart.open_unprotected(@name) do |doc|
76
+ doc.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
77
+ block.call doc
78
+ end
79
+ end
80
+ end #}}}
81
+
82
+ class Subs #{{{
83
+ def initialize(target)
84
+ @target = target
85
+ end
86
+
87
+ def each(&block)
88
+ keys.each do |key|
89
+ doc = XML::Smart.open_unprotected(@target + '/' + key + '/subscription.xml')
90
+ doc.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
91
+ block.call doc, key
92
+ end
93
+ end
94
+
95
+ def [](key)
96
+ f = @target + '/' + key + '/subscription.xml'
97
+ File.exists?(f) ? Sub.new(f) : nil
98
+ end
99
+
100
+ def create(&block)
101
+ key = nil
102
+ begin
103
+ continue = true
104
+ key = Digest::MD5.hexdigest(Kernel::rand().to_s)
105
+ Dir.mkdir(@target + '/' + key) rescue continue = false
106
+ end until continue
107
+ producer_secret = Digest::MD5.hexdigest(Kernel::rand().to_s)
108
+ consumer_secret = Digest::MD5.hexdigest(Kernel::rand().to_s)
109
+ File.open(@target + '/' + key + '/producer-secret','w') { |f| f.write producer_secret }
110
+ File.open(@target + '/' + key + '/consumer-secret','w') { |f| f.write consumer_secret }
111
+ XML::Smart::modify(@target + '/' + key + '/subscription.xml',"<subscription xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'/>") do |doc|
112
+ block.call doc, key
113
+ end
114
+ [key, producer_secret, consumer_secret]
115
+ end
116
+
117
+ def keys
118
+ if File.directory?(@target)
119
+ Dir[@target + '/*'].map do |d|
120
+ File.directory?(d) ? File.basename(d) : nil
121
+ end.compact
122
+ else
123
+ []
124
+ end
125
+ end
126
+ end #}}}
127
+
128
+ def initialize(topics,target)
129
+ @target = target.gsub(/^\/+/,'/')
130
+
131
+ FileUtils::mkdir_p(@target) unless File.exists?(@target)
132
+
133
+ raise "topics file not found" unless File.exists?(topics)
134
+ @topics = XML::Smart.open_unprotected(topics.gsub(/^\/+/,'/'))
135
+ @topics.register_namespace 'n', 'http://riddl.org/ns/common-patterns/notifications-producer/1.0'
136
+ end
137
+
138
+ def subscriptions
139
+ Subs.new(@target)
140
+ end
141
+
142
+ end #}}}
143
+
144
+ class Overview < Riddl::Implementation #{{{
145
+ def response
146
+ Riddl::Parameter::Complex.new("overview","text/xml") do
147
+ <<-END
148
+ <overview xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'>
149
+ <topics/>
150
+ <subscriptions/>
151
+ </overview>
152
+ END
153
+ end
154
+
155
+ end
156
+ end #}}}
157
+
158
+ class Topics < Riddl::Implementation #{{{
159
+ def response
160
+ backend = @a[0]
161
+ Riddl::Parameter::Complex.new("overview","text/xml") do
162
+ backend.topics.to_s
163
+ end
164
+ end
165
+ end #}}}
166
+
167
+ class Subscriptions < Riddl::Implementation #{{{
168
+ def response
169
+ backend = @a[0]
170
+ details = @a[1]
171
+ Riddl::Parameter::Complex.new("subscriptions","text/xml") do
172
+ ret = XML::Smart::string <<-END
173
+ <subscriptions details='#{details}' xmlns='http://riddl.org/ns/common-patterns/notifications-producer/1.0'/>
174
+ END
175
+ backend.subscriptions.each do |doc,key|
176
+ if doc.root.attributes['url']
177
+ ret.root.add('subscription', :id => key, :url => doc.root.attributes['url'])
178
+ else
179
+ ret.root.add('subscription', :id => key)
180
+ end
181
+ end
182
+ ret.to_s
183
+ end
184
+ end
185
+ end #}}}
186
+
187
+ class Subscription < Riddl::Implementation #{{{
188
+ def response
189
+ backend = @a[0]
190
+ Riddl::Parameter::Complex.new("subscription","text/xml") do
191
+ backend.subscriptions[@r.last].to_s
192
+ end
193
+ end
194
+ end #}}}
195
+
196
+ class CreateSubscription < Riddl::Implementation #{{{
197
+ def response
198
+ backend = @a[0]
199
+ handler = @a[1]
200
+
201
+ url = @p[0].name == 'url' ? @p.shift.value : nil
202
+
203
+ topics = []
204
+ key, consumer_secret, producer_secret = backend.subscriptions.create do |doc,key|
205
+ doc.root.attributes['url'] = url if url
206
+ while @p.length > 0
207
+ topic = @p.shift.value
208
+ base = @p.shift
209
+ type = base.name
210
+ items = base.value.split(',')
211
+ t = if topics.include?(topic)
212
+ doc.find("/n:subscription/n:topic[@id='#{topic}']").first
213
+ else
214
+ topics << topic
215
+ doc.root.add('topic', :id => topic)
216
+ end
217
+ items.each do |i|
218
+ t.add(type[0..-2], i)
219
+ end
220
+ end
221
+ end
222
+
223
+ handler.key(key).topics(topics).create unless handler.nil?
224
+ [
225
+ Riddl::Parameter::Simple.new('key',key),
226
+ Riddl::Parameter::Simple.new('producer-secret',producer_secret),
227
+ Riddl::Parameter::Simple.new('consumer-secret',consumer_secret)
228
+ ]
229
+ end
230
+ end #}}}
231
+
232
+ class DeleteSubscription < Riddl::Implementation #{{{
233
+ def response
234
+ backend = @a[0]
235
+ handler = @a[1]
236
+ key = @r.last
237
+
238
+ backend.subscriptions[key].delete
239
+ handler.key(key).delete unless handler.nil?
240
+ return
241
+ end
242
+ end #}}}
243
+
244
+ class UpdateSubscription < Riddl::Implementation #{{{
245
+ def response
246
+ backend = @a[0]
247
+ handler = @a[1]
248
+ key = @r.last
249
+
250
+ muid = @p.shift.value
251
+ url = @p[0].name == 'url' ? @p.shift.value : nil
252
+
253
+ # TODO check if message is valid (with producer secret)
254
+ unless backend.subscriptions[key]
255
+ @status = 404
256
+ return # subscription not found
257
+ end
258
+
259
+ topics = []
260
+ backend.subscriptions[key].modify do |doc|
261
+ if url.nil?
262
+ doc.find('/n:subscription/@url').delete_all!
263
+ else
264
+ doc.root.attributes['url'] = url
265
+ end
266
+ doc.root.children.delete_all!
267
+ while @p.length > 1
268
+ topic = @p.shift.value
269
+ base = @p.shift
270
+ type = base.name
271
+ items = base.value.split(',')
272
+ t = if topics.include?(topic)
273
+ doc.find("/n:subscription/n:topic[@id='#{topic}']").first
274
+ else
275
+ topics << topic
276
+ doc.root.add('topic', :id => topic)
277
+ end
278
+ items.each do |i|
279
+ t.add(type[0..-2], i)
280
+ end
281
+ end
282
+ end
283
+
284
+ handler.key(key).topics(topics).update unless handler.nil?
285
+ nil
286
+ end
287
+ end #}}}
288
+
289
+ class WS < Riddl::WebSocketImplementation #{{{
290
+ def onopen
291
+ @backend = @a[0]
292
+ @handler = @a[1]
293
+ @key = @r[-2]
294
+ @handler.key(@key).ws_open(self) unless @handler.nil?
295
+ end
296
+
297
+ def onmessage(data)
298
+ @handler.key(@key).ws_message(data) unless @handler.nil?
299
+ end
300
+
301
+ def onclose
302
+ @handler.key(@key).ws_close() unless @handler.nil?
303
+ end
304
+ end #}}}
305
+
306
+ end
307
+
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,474 @@
1
+ module Riddl
2
+ module Utils
3
+ module Properties
4
+
5
+ VERSION_MAJOR = 1
6
+ VERSION_MINOR = 0
7
+ PROPERTIES_SCHEMA_XSL_RNG = "#{File.dirname(__FILE__)}/../ns/common-patterns/properties/#{VERSION_MAJOR}.#{VERSION_MINOR}/properties.schema.xsl"
8
+
9
+ def self::implementation(backend,handler=nil,details=:production)
10
+ unless handler.nil? || (handler.is_a? Riddl::Utils::Properties::HandlerBase)
11
+ raise "handler not a subclass of HandlerBase"
12
+ end
13
+ Proc.new do
14
+ run Riddl::Utils::Properties::All, backend, handler if get '*'
15
+ run Riddl::Utils::Properties::Query, backend, handler if get 'query'
16
+ on resource 'schema' do
17
+ run Riddl::Utils::Properties::Schema, backend if get
18
+ on resource 'rng' do
19
+ run Riddl::Utils::Properties::RngSchema, backend if get
20
+ end
21
+ end
22
+ on resource 'values' do
23
+ run Riddl::Utils::Properties::Properties, backend, handler if get
24
+ run Riddl::Utils::Properties::AddProperty, backend, handler if post 'property'
25
+ run Riddl::Utils::Properties::AddProperties, backend, handler if put 'properties'
26
+ on resource do
27
+ run Riddl::Utils::Properties::GetContent, backend, handler if get
28
+ run Riddl::Utils::Properties::DelContent, backend, handler if delete
29
+ run Riddl::Utils::Properties::AddContent, backend, handler if post 'addcontent'
30
+ run Riddl::Utils::Properties::UpdContent, backend, handler if put 'updcontent'
31
+ on resource do
32
+ run Riddl::Utils::Properties::GetContent, backend, handler if get
33
+ run Riddl::Utils::Properties::DelContent, backend, handler if delete
34
+ run Riddl::Utils::Properties::UpdContent, backend, handler if put 'updcontent'
35
+ on resource do
36
+ run Riddl::Utils::Properties::GetContent, backend, handler if get
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # Overloadable and Backends
45
+ class HandlerBase #{{{
46
+ def initialize(data)
47
+ @data = data
48
+ @property = nil
49
+ end
50
+ def property(p)
51
+ @property = p
52
+ self
53
+ end
54
+ def create; end
55
+ def read; end
56
+ def update; end
57
+ def delete; end
58
+ end #}}}
59
+
60
+ class Backend #{{{
61
+ attr_reader :schema, :data, :rng
62
+
63
+ def initialize(schema,target,init=nil)
64
+ @target = target.gsub(/^\/+/,'/')
65
+ @schemas = {}
66
+ @rngs = {}
67
+
68
+ if schema.is_a? Hash
69
+ schema.each { |k,v| add_schema k, v }
70
+ elsif schema.is_a? String
71
+ add_schema 'default', schema
72
+ end
73
+ raise "no schemas provided" if @schemas.length == 0
74
+ @schema = @schemas.first[1]
75
+ @rng = @rngs.first[1]
76
+
77
+ FileUtils::mkdir_p(File::dirname(@target)) unless File.exists?(@target)
78
+ FileUtils::cp init, @target if init and not File.exists?(@target)
79
+
80
+ raise "properties file not found" unless File.exists?(@target)
81
+ @data = XML::Smart.open_unprotected(@target)
82
+ @data.register_namespace 'p', 'http://riddl.org/ns/common-patterns/properties/1.0'
83
+ @mutex = Mutex.new
84
+ end
85
+
86
+ def activate_schema(name)
87
+ if @schemas[name]
88
+ @schema = @schemas[name]
89
+ @rng = @rngs[name]
90
+ true
91
+ else
92
+ false
93
+ end
94
+ end
95
+
96
+ def add_schema(key,name)
97
+ raise "schema file not found" unless File.exists?(name)
98
+ @schemas[key] = XML::Smart.open_unprotected(name.gsub(/^\/+/,'/'))
99
+ @schemas[key].register_namespace 'p', 'http://riddl.org/ns/common-patterns/properties/1.0'
100
+ if !File::exists?(Riddl::Utils::Properties::PROPERTIES_SCHEMA_XSL_RNG)
101
+ raise "properties schema transformation file not found"
102
+ end
103
+ @rngs[key] = @schemas[key].transform_with(XML::Smart.open_unprotected(Riddl::Utils::Properties::PROPERTIES_SCHEMA_XSL_RNG))
104
+ end
105
+ private :add_schema
106
+
107
+ def modifiable?(property)
108
+ @schema.find("boolean(/p:properties/p:#{property}[@modifiable='true'])") || schema.find("boolean(/p:properties/p:optional/p:#{property}[@modifiable='true'])")
109
+ end
110
+ def valid_state?(property,current,new)
111
+ @schema.find("boolean(/p:properties/p:#{property}/p:#{current}/p:#{new}[@putable='true'])") || schema.find("boolean(/p:properties/p:optional/p:#{property}/p:#{current}/p:#{new}[@putable='true'])")
112
+ end
113
+ def is_state?(property)
114
+ @schema.find("boolean(/p:properties/p:#{property}[@type='state'])") || schema.find("boolean(/p:properties/p:optional/p:#{property}[@type='state'])")
115
+ end
116
+ def init_state?(property,new)
117
+ @schema.find("boolean(/p:properties/p:#{property}/p:#{new}[position()=1])") || schema.find("boolean(/p:properties/p:optional/p:#{property}/p:#{new}[position()=1])")
118
+ end
119
+ def property_type(property)
120
+ exis = @schema.find("/p:properties/*[name()='#{property}']|/p:properties/p:optional/*[name()='#{property}']")
121
+ exis.any? ? exis.first.attributes['type'].to_sym : nil
122
+ end
123
+
124
+ def modify(&block)
125
+ tdoc = @data.root.to_doc
126
+ tdoc.register_namespace 'p', 'http://riddl.org/ns/common-patterns/properties/1.0'
127
+ @mutex.synchronize do
128
+ block.call tdoc
129
+ if tdoc.validate_against(@rng)
130
+ block.call @data
131
+ @data.save_as(@target)
132
+ true
133
+ else
134
+ false
135
+ end
136
+ end
137
+ end
138
+ end #}}}
139
+
140
+ # Just reading
141
+ class All < Riddl::Implementation #{{{
142
+ def response
143
+ backend = @a[0]
144
+ handler = @a[1]
145
+ handler.read unless handler.nil?
146
+ return Riddl::Parameter::Complex.new("document","text/xml",backend.data.to_s)
147
+ end
148
+ end #}}}
149
+
150
+ class Properties < Riddl::Implementation #{{{
151
+ def response
152
+ backend = @a[0]
153
+ handler = @a[1]
154
+ handler.read unless handler.nil?
155
+
156
+ ret = XML::Smart.string("<properties xmlns=\"http://riddl.org/ns/common-patterns/properties/1.0\"/>")
157
+ backend.schema.find("/p:properties/*[name()!='optional']|/p:properties/p:optional/*").each do |r|
158
+ ret.root.add("property",r.qname.to_s)
159
+ end
160
+ return Riddl::Parameter::Complex.new("keys","text/xml",ret.to_s)
161
+ end
162
+ end #}}}
163
+
164
+ class Query < Riddl::Implementation #{{{
165
+ def response
166
+ backend = @a[0]
167
+ handler = @a[1]
168
+ handler.read unless handler.nil?
169
+ query = (@p[0].value.to_s.strip.empty? ? '*' : @p[0].value)
170
+
171
+ begin
172
+ e = backend.data.find(query)
173
+ rescue => e
174
+ prop = XML::Smart::string("<not-existing xmlns=\"http://riddl.org/ns/common-patterns/properties/1.0\"/>").to_s
175
+ return Riddl::Parameter::Complex.new("value","text/xml",prop.to_s)
176
+ end
177
+ if e.class == XML::Smart::Dom::NodeSet
178
+ if e.any?
179
+ prop = XML::Smart::string("<value xmlns=\"http://riddl.org/ns/common-patterns/properties/1.0\"/>")
180
+ prop.root.add(e)
181
+ else
182
+ prop = XML::Smart::string("<not-existing xmlns=\"http://riddl.org/ns/common-patterns/properties/1.0\"/>").to_s
183
+ end
184
+ return Riddl::Parameter::Complex.new("value","text/xml",prop.to_s)
185
+ else
186
+ return Riddl::Parameter::Simple.new("value",e.to_s)
187
+ end
188
+ end
189
+ end #}}}
190
+
191
+ class RngSchema < Riddl::Implementation #{{{
192
+ def response
193
+ backend = @a[0]
194
+ Riddl::Parameter::Complex.new("document-schema","text/xml",backend.rng.to_s)
195
+ end
196
+ end #}}}
197
+
198
+ class Schema < Riddl::Implementation #{{{
199
+ def response
200
+ backend = @a[0]
201
+ return Riddl::Parameter::Complex.new("document-schema","text/xml",backend.schema.to_s)
202
+ end
203
+ end #}}}
204
+
205
+ class GetContent < Riddl::Implementation #{{{
206
+ def response
207
+ backend = @a[0]
208
+ handler = @a[1]
209
+
210
+ handler.property(@r[1]).read unless handler.nil?
211
+
212
+ if ret = extract_values(backend,@r[1],Riddl::Protocols::HTTP::Parser::unescape(@r[2..-1].join('/')))
213
+ ret
214
+ else
215
+ @status = 404
216
+ end
217
+ end
218
+
219
+ def extract_values(backend,property,minor=nil)
220
+ case backend.property_type(property)
221
+ when :complex
222
+ res = backend.data.find("/p:properties/*[name()=\"#{property}\"]#{minor == '' ? '' : "/p:#{minor}"}")
223
+ if res.any?
224
+ prop = XML::Smart::string("<value xmlns=\"http://riddl.org/ns/common-patterns/properties/1.0\"/>")
225
+ if res.length == 1
226
+ prop.root.add(res.first.children)
227
+ else
228
+ prop.root.add(res)
229
+ end
230
+ return Riddl::Parameter::Complex.new("value","text/xml",prop.to_s)
231
+ else
232
+ prop = XML::Smart::string("<not-existing xmlns=\"http://riddl.org/ns/common-patterns/properties/1.0\"/>")
233
+ end
234
+ when :simple, :state
235
+ res = backend.data.find("string(/p:properties/*[name()=\"#{property}\"]#{minor})")
236
+ return Riddl::Parameter::Simple.new("value",res.to_s)
237
+ when :arbitrary
238
+ res = backend.data.find("/p:properties/*[name()=\"#{property}\"]")
239
+ if res.any?
240
+ c = res.first.children
241
+ if c.length == 1 && c.first.class == XML::Smart::Dom::Element
242
+ return Riddl::Parameter::Complex.new("content","text/xml",c.first.dump)
243
+ else
244
+ return Riddl::Parameter::Complex.new("content","text/plain",c.first.to_s)
245
+ end
246
+ else
247
+ prop = XML::Smart::string("<not-existing xmlns=\"http://riddl.org/ns/common-patterns/properties/1.0\"/>")
248
+ return Riddl::Parameter::Complex.new("content","text/xml",prop.to_s)
249
+ end
250
+ end
251
+ nil
252
+ end
253
+ private :extract_values
254
+
255
+ end #}}}
256
+
257
+ # Modifiable
258
+ class AddProperty < Riddl::Implementation #{{{
259
+ def response
260
+ backend = @a[0]
261
+ handler = @a[1]
262
+
263
+ property = @p[0].value
264
+ ct = @p[1]
265
+ value = ct.name == 'value' ? ct.value : nil
266
+ content = ct.name == 'content' ? ct.value : nil
267
+
268
+ unless backend.modifiable?(property)
269
+ @status = 500
270
+ return # change properties.schema
271
+ end
272
+
273
+ path = "/p:properties/*[name()=\"#{property}\"]"
274
+ nodes = backend.data.find(path)
275
+ if nodes.any?
276
+ @status = 404
277
+ return # this property does not exist
278
+ end
279
+
280
+ if backend.is_state?(property)
281
+ unless backend.init_state?(property,value)
282
+ @status = 404
283
+ return # not a valid state from here on
284
+ end
285
+ end
286
+
287
+ newstuff = value.nil? ? XML::Smart.string(content).root.children : value
288
+ backend.modify do |doc|
289
+ ele = doc.root.add property
290
+ if value.nil?
291
+ ele.add newstuff
292
+ else
293
+ ele.text = newstuff
294
+ end
295
+ end || begin
296
+ @status = 400
297
+ return # bad request
298
+ end
299
+
300
+ handler.property(property).create unless handler.nil?
301
+ return
302
+ end
303
+ end #}}}
304
+
305
+ class AddProperties < Riddl::Implementation #{{{
306
+ def response
307
+ backend = @a[0]
308
+ handler = @a[1]
309
+
310
+ 0.upto(@p.length/2-1) do |i|
311
+ property = @p[i*2].value
312
+ ct = @p[i*2+1]
313
+ value = ct.name == 'value' ? ct.value : nil
314
+ content = ct.name == 'content' ? ct.value : nil
315
+
316
+ unless backend.modifiable?(property)
317
+ @status = 500
318
+ return # change properties.schema
319
+ end
320
+
321
+ path = "/p:properties/*[name()=\"#{property}\"]"
322
+ nodes = backend.data.find(path)
323
+ if nodes.empty?
324
+ @status = 404
325
+ return # this property does not exist
326
+ end
327
+
328
+ if backend.is_state?(property)
329
+ unless backend.valid_state?(property,nodes.first.to_s,value)
330
+ @status = 404
331
+ return # not a valid state from here on
332
+ end
333
+ end
334
+
335
+ newstuff = value.nil? ? XML::Smart.string(content).root.children : value
336
+ backend.modify do |doc|
337
+ nodes = doc.find(path)
338
+ nods = nodes.map{|ele| ele.children.delete_all!; ele}
339
+ nods.each do |ele|
340
+ if value.nil?
341
+ ele.add newstuff
342
+ else
343
+ ele.text = newstuff
344
+ end
345
+ end
346
+ end || begin
347
+ @status = 400
348
+ return # bad request
349
+ end
350
+
351
+ handler.property(property).create unless handler.nil?
352
+ end
353
+ return
354
+ end
355
+ end #}}}
356
+
357
+ class AddContent < Riddl::Implementation #{{{
358
+ def response
359
+ backend = @a[0]
360
+ handler = @a[1]
361
+
362
+ property = @r[1]
363
+ value = @p.detect{|p| p.name == 'value'}.value
364
+
365
+ unless backend.modifiable?(property)
366
+ @status = 500
367
+ return # change properties.schema
368
+ end
369
+
370
+ path = "/p:properties/p:#{property}"
371
+ node = backend.data.find(path)
372
+ if node.empty?
373
+ @status = 404
374
+ return # this property does not exist
375
+ end
376
+
377
+ newstuff = XML::Smart.string(value)
378
+ backend.modify do |doc|
379
+ node = doc.find(path)
380
+ node.first.add newstuff.root
381
+ end || begin
382
+ @status = 400
383
+ return # bad request
384
+ end
385
+
386
+ handler.property(property).create unless handler.nil?
387
+ end
388
+ end #}}}
389
+
390
+ class DelContent < Riddl::Implementation #{{{
391
+ def response
392
+ backend = @a[0]
393
+ handler = @a[1]
394
+
395
+ property = @r[1]
396
+ minor = Riddl::Protocols::HTTP::Parser::unescape(@r[2])
397
+
398
+ unless backend.modifiable?(property)
399
+ @status = 500
400
+ return # change properties.schema
401
+ end
402
+
403
+ path = "/p:properties/*[name()=\"#{property}\"]#{minor.nil? ? '' : "/p:#{minor}"}"
404
+ nodes = backend.data.find(path)
405
+ if nodes.empty?
406
+ @status = 404
407
+ return # this property does not exist
408
+ end
409
+
410
+ backend.modify do |doc|
411
+ doc.find(path).delete_all!
412
+ end || begin
413
+ @status = 400
414
+ return # bad request
415
+ end
416
+
417
+ handler.property(property).delete unless handler.nil?
418
+ return
419
+ end
420
+ end #}}}
421
+
422
+ class UpdContent < Riddl::Implementation #{{{
423
+ def response
424
+ backend = @a[0]
425
+ handler = @a[1]
426
+
427
+ property = @r[1]
428
+ value = @p.detect{|p| p.name == 'value'}; value = value.nil? ? value : value.value
429
+ content = @p.detect{|p| p.name == 'content'}; content = content.nil? ? content : content.value
430
+ minor = @r[2]
431
+
432
+ unless backend.modifiable?(property)
433
+ @status = 500
434
+ return # change properties.schema
435
+ end
436
+
437
+ path = "/p:properties/*[name()=\"#{property}\"]#{minor.nil? ? '' : "/p:#{minor}"}"
438
+ nodes = backend.data.find(path)
439
+ if nodes.empty?
440
+ @status = 404
441
+ return # this property does not exist
442
+ end
443
+
444
+ if backend.is_state?(property)
445
+ unless backend.valid_state?(property,nodes.first.to_s,value)
446
+ @status = 404
447
+ return # not a valid state from here on
448
+ end
449
+ end
450
+
451
+ newstuff = value.nil? ? XML::Smart.string(content).root.children : value
452
+ backend.modify do |doc|
453
+ nodes = doc.root.find(path)
454
+ nods = nodes.map{|ele| ele.children.delete_all!; ele}
455
+ nods.each do |ele|
456
+ if value.nil?
457
+ ele.add newstuff
458
+ else
459
+ ele.text = newstuff
460
+ end
461
+ end
462
+ end || begin
463
+ @status = 400
464
+ return # bad request
465
+ end
466
+
467
+ handler.property(property).update unless handler.nil?
468
+ return
469
+ end
470
+ end #}}}
471
+
472
+ end
473
+ end
474
+ end