pione 0.1.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 (366) hide show
  1. data/.gitignore +14 -0
  2. data/Gemfile +5 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +94 -0
  5. data/Rakefile +118 -0
  6. data/bin/pione-broker +5 -0
  7. data/bin/pione-clean +5 -0
  8. data/bin/pione-client +5 -0
  9. data/bin/pione-eval +111 -0
  10. data/bin/pione-relay +5 -0
  11. data/bin/pione-relay-account-db +5 -0
  12. data/bin/pione-relay-client-db +5 -0
  13. data/bin/pione-search-log +30 -0
  14. data/bin/pione-syntax-checker +5 -0
  15. data/bin/pione-task-worker +5 -0
  16. data/bin/pione-tuple-space-provider +5 -0
  17. data/bin/pione-tuple-space-receiver +5 -0
  18. data/bin/pione-tuple-space-viewer +5 -0
  19. data/demo/demo.rb +311 -0
  20. data/demo/public/base.css +94 -0
  21. data/demo/public/demo.js +107 -0
  22. data/demo/public/index.html +91 -0
  23. data/demo/public/jquery-1.8.3.min.js +2 -0
  24. data/example/CountChar/CountChar.pione +64 -0
  25. data/example/CountChar/misc/CountChar.rb +22 -0
  26. data/example/CountChar/text/aidokushono_insho.txt +32 -0
  27. data/example/CountChar/text/aikokuka_shokan.txt +108 -0
  28. data/example/CountChar/text/carlyle_hakubutsukan.txt +58 -0
  29. data/example/CountChar/text/dark_minister.txt +2440 -0
  30. data/example/CountChar/text/kaikano_otto.txt +61 -0
  31. data/example/CountChar/text/kaikon.txt +30 -0
  32. data/example/CountChar/text/nagashimano_shi.txt +45 -0
  33. data/example/CountChar/text/saikachibuchi.txt +80 -0
  34. data/example/CountChar/text/saikonihonno_josei.txt +91 -0
  35. data/example/CountChar/text/taishojugonenno_bundan.txt +21 -0
  36. data/example/FeatureExample/FeatureExample.pione +7 -0
  37. data/example/Fib/Fib.pione +56 -0
  38. data/example/Fib/FibBC.pione +56 -0
  39. data/example/HelloWorld/HelloWorld.pione +5 -0
  40. data/example/LucasNumber/LucasNumber.pione +64 -0
  41. data/example/MakePair/MakePair.pione +14 -0
  42. data/example/MakePair/data/1.i +0 -0
  43. data/example/MakePair/data/2.i +0 -0
  44. data/example/MakePair/data/3.i +0 -0
  45. data/example/MakePair/data/4.i +0 -0
  46. data/example/MakePair/data/5.i +0 -0
  47. data/example/SieveOfEratosthenes/SieveOfEratosthenes.pione +61 -0
  48. data/example/SingleParticlesWithRef/Makefile +289 -0
  49. data/example/SingleParticlesWithRef/SingleParticlesWithRef.Makefile +153 -0
  50. data/example/SingleParticlesWithRef/SingleParticlesWithRef.pione +116 -0
  51. data/example/SingleParticlesWithRef/SingleParticlesWithRefFull.pione +400 -0
  52. data/example/SingleParticlesWithRef/data/121p-shift-0-0-0.roi +0 -0
  53. data/example/SingleParticlesWithRef/data/121p-shift-0-120-0.roi +0 -0
  54. data/example/SingleParticlesWithRef/data/121p-shift-0-180-0.roi +0 -0
  55. data/example/SingleParticlesWithRef/data/121p-shift-0-240-0.roi +0 -0
  56. data/example/SingleParticlesWithRef/data/121p-shift-0-300-0.roi +0 -0
  57. data/example/SingleParticlesWithRef/data/121p-shift-0-60-0.roi +0 -0
  58. data/example/SingleParticlesWithRef/data/121p-shift-120-0-0.roi +0 -0
  59. data/example/SingleParticlesWithRef/data/121p-shift-120-120-0.roi +0 -0
  60. data/example/SingleParticlesWithRef/data/121p-shift-120-180-0.roi +0 -0
  61. data/example/SingleParticlesWithRef/data/121p-shift-120-240-0.roi +0 -0
  62. data/example/SingleParticlesWithRef/data/121p-shift-120-300-0.roi +0 -0
  63. data/example/SingleParticlesWithRef/data/121p-shift-120-60-0.roi +0 -0
  64. data/example/SingleParticlesWithRef/data/121p-shift-180-0-0.roi +0 -0
  65. data/example/SingleParticlesWithRef/data/121p-shift-180-120-0.roi +0 -0
  66. data/example/SingleParticlesWithRef/data/121p-shift-180-180-0.roi +0 -0
  67. data/example/SingleParticlesWithRef/data/121p-shift-180-240-0.roi +0 -0
  68. data/example/SingleParticlesWithRef/data/121p-shift-180-300-0.roi +0 -0
  69. data/example/SingleParticlesWithRef/data/121p-shift-180-60-0.roi +0 -0
  70. data/example/SingleParticlesWithRef/data/121p-shift-240-0-0.roi +0 -0
  71. data/example/SingleParticlesWithRef/data/121p-shift-240-120-0.roi +0 -0
  72. data/example/SingleParticlesWithRef/data/121p-shift-240-180-0.roi +0 -0
  73. data/example/SingleParticlesWithRef/data/121p-shift-240-240-0.roi +0 -0
  74. data/example/SingleParticlesWithRef/data/121p-shift-240-300-0.roi +0 -0
  75. data/example/SingleParticlesWithRef/data/121p-shift-240-60-0.roi +0 -0
  76. data/example/SingleParticlesWithRef/data/121p-shift-300-0-0.roi +0 -0
  77. data/example/SingleParticlesWithRef/data/121p-shift-300-120-0.roi +0 -0
  78. data/example/SingleParticlesWithRef/data/121p-shift-300-180-0.roi +0 -0
  79. data/example/SingleParticlesWithRef/data/121p-shift-300-240-0.roi +0 -0
  80. data/example/SingleParticlesWithRef/data/121p-shift-300-300-0.roi +0 -0
  81. data/example/SingleParticlesWithRef/data/121p-shift-300-60-0.roi +0 -0
  82. data/example/SingleParticlesWithRef/data/121p-shift-60-0-0.roi +0 -0
  83. data/example/SingleParticlesWithRef/data/121p-shift-60-120-0.roi +0 -0
  84. data/example/SingleParticlesWithRef/data/121p-shift-60-180-0.roi +0 -0
  85. data/example/SingleParticlesWithRef/data/121p-shift-60-240-0.roi +0 -0
  86. data/example/SingleParticlesWithRef/data/121p-shift-60-300-0.roi +0 -0
  87. data/example/SingleParticlesWithRef/data/121p-shift-60-60-0.roi +0 -0
  88. data/example/SingleParticlesWithRef/data/121p-shift-noise-0-0-0.roi +0 -0
  89. data/example/SingleParticlesWithRef/data/121p-shift-noise-0-120-0.roi +0 -0
  90. data/example/SingleParticlesWithRef/data/121p-shift-noise-0-180-0.roi +0 -0
  91. data/example/SingleParticlesWithRef/data/121p-shift-noise-0-240-0.roi +0 -0
  92. data/example/SingleParticlesWithRef/data/121p-shift-noise-0-300-0.roi +0 -0
  93. data/example/SingleParticlesWithRef/data/121p-shift-noise-0-60-0.roi +0 -0
  94. data/example/SingleParticlesWithRef/data/121p-shift-noise-120-0-0.roi +0 -0
  95. data/example/SingleParticlesWithRef/data/121p-shift-noise-120-120-0.roi +0 -0
  96. data/example/SingleParticlesWithRef/data/121p-shift-noise-120-180-0.roi +0 -0
  97. data/example/SingleParticlesWithRef/data/121p-shift-noise-120-240-0.roi +0 -0
  98. data/example/SingleParticlesWithRef/data/121p-shift-noise-120-300-0.roi +0 -0
  99. data/example/SingleParticlesWithRef/data/121p-shift-noise-120-60-0.roi +0 -0
  100. data/example/SingleParticlesWithRef/data/121p-shift-noise-180-0-0.roi +0 -0
  101. data/example/SingleParticlesWithRef/data/121p-shift-noise-180-120-0.roi +0 -0
  102. data/example/SingleParticlesWithRef/data/121p-shift-noise-180-180-0.roi +0 -0
  103. data/example/SingleParticlesWithRef/data/121p-shift-noise-180-240-0.roi +0 -0
  104. data/example/SingleParticlesWithRef/data/121p-shift-noise-180-300-0.roi +0 -0
  105. data/example/SingleParticlesWithRef/data/121p-shift-noise-180-60-0.roi +0 -0
  106. data/example/SingleParticlesWithRef/data/121p-shift-noise-240-0-0.roi +0 -0
  107. data/example/SingleParticlesWithRef/data/121p-shift-noise-240-120-0.roi +0 -0
  108. data/example/SingleParticlesWithRef/data/121p-shift-noise-240-180-0.roi +0 -0
  109. data/example/SingleParticlesWithRef/data/121p-shift-noise-240-240-0.roi +0 -0
  110. data/example/SingleParticlesWithRef/data/121p-shift-noise-240-300-0.roi +0 -0
  111. data/example/SingleParticlesWithRef/data/121p-shift-noise-240-60-0.roi +0 -0
  112. data/example/SingleParticlesWithRef/data/121p-shift-noise-300-0-0.roi +0 -0
  113. data/example/SingleParticlesWithRef/data/121p-shift-noise-300-120-0.roi +0 -0
  114. data/example/SingleParticlesWithRef/data/121p-shift-noise-300-180-0.roi +0 -0
  115. data/example/SingleParticlesWithRef/data/121p-shift-noise-300-240-0.roi +0 -0
  116. data/example/SingleParticlesWithRef/data/121p-shift-noise-300-300-0.roi +0 -0
  117. data/example/SingleParticlesWithRef/data/121p-shift-noise-300-60-0.roi +0 -0
  118. data/example/SingleParticlesWithRef/data/121p-shift-noise-60-0-0.roi +0 -0
  119. data/example/SingleParticlesWithRef/data/121p-shift-noise-60-120-0.roi +0 -0
  120. data/example/SingleParticlesWithRef/data/121p-shift-noise-60-180-0.roi +0 -0
  121. data/example/SingleParticlesWithRef/data/121p-shift-noise-60-240-0.roi +0 -0
  122. data/example/SingleParticlesWithRef/data/121p-shift-noise-60-300-0.roi +0 -0
  123. data/example/SingleParticlesWithRef/data/121p-shift-noise-60-60-0.roi +0 -0
  124. data/example/SingleParticlesWithRef/data/121p-shift.pdb +3381 -0
  125. data/example/SingleParticlesWithRef/data/all.ref2d +0 -0
  126. data/example/SingleParticlesWithRef/data/all.ref3d +0 -0
  127. data/example/Sum/Sum.pione +52 -0
  128. data/example/SyntaxError/call_rule_error.pione +6 -0
  129. data/example/SyntaxError/feature_line_error.pione +7 -0
  130. data/example/SyntaxError/flow_block_error.pione +5 -0
  131. data/example/SyntaxError/input_line_error.pione +6 -0
  132. data/example/SyntaxError/invalid_rule_name.pione +6 -0
  133. data/example/SyntaxError/param_line_error.pione +7 -0
  134. data/example/SyntaxError/variable-binding-error.pione +11 -0
  135. data/lib/pione.rb +241 -0
  136. data/lib/pione/agent/basic-agent.rb +333 -0
  137. data/lib/pione/agent/broker.rb +274 -0
  138. data/lib/pione/agent/command-listener.rb +47 -0
  139. data/lib/pione/agent/input-generator.rb +194 -0
  140. data/lib/pione/agent/logger.rb +65 -0
  141. data/lib/pione/agent/process-manager.rb +38 -0
  142. data/lib/pione/agent/rule-provider.rb +64 -0
  143. data/lib/pione/agent/task-worker.rb +274 -0
  144. data/lib/pione/agent/trivial-routine-worker.rb +28 -0
  145. data/lib/pione/agent/tuple-space-client.rb +146 -0
  146. data/lib/pione/agent/tuple-space-server-client-life-checker.rb +29 -0
  147. data/lib/pione/command-option/basic-option.rb +42 -0
  148. data/lib/pione/command-option/child-process-option.rb +17 -0
  149. data/lib/pione/command-option/common-option.rb +29 -0
  150. data/lib/pione/command-option/daemon-option.rb +12 -0
  151. data/lib/pione/command-option/presence-notifier-option.rb +15 -0
  152. data/lib/pione/command-option/task-worker-owner-option.rb +17 -0
  153. data/lib/pione/command-option/tuple-space-provider-option.rb +26 -0
  154. data/lib/pione/command-option/tuple-space-provider-owner-option.rb +16 -0
  155. data/lib/pione/command-option/tuple-space-receiver-option.rb +12 -0
  156. data/lib/pione/command/basic-command.rb +126 -0
  157. data/lib/pione/command/child-process.rb +43 -0
  158. data/lib/pione/command/daemon-process.rb +18 -0
  159. data/lib/pione/command/front-owner-command.rb +37 -0
  160. data/lib/pione/command/pione-broker.rb +53 -0
  161. data/lib/pione/command/pione-clean.rb +16 -0
  162. data/lib/pione/command/pione-client.rb +273 -0
  163. data/lib/pione/command/pione-relay-account-db.rb +85 -0
  164. data/lib/pione/command/pione-relay-client-db.rb +80 -0
  165. data/lib/pione/command/pione-relay.rb +47 -0
  166. data/lib/pione/command/pione-syntax-checker.rb +103 -0
  167. data/lib/pione/command/pione-task-worker.rb +123 -0
  168. data/lib/pione/command/pione-tuple-space-provider.rb +87 -0
  169. data/lib/pione/command/pione-tuple-space-receiver.rb +55 -0
  170. data/lib/pione/command/pione-tuple-space-viewer.rb +151 -0
  171. data/lib/pione/command/tuple-space-provider-owner.rb +6 -0
  172. data/lib/pione/front/basic-front.rb +64 -0
  173. data/lib/pione/front/broker-front.rb +30 -0
  174. data/lib/pione/front/client-front.rb +28 -0
  175. data/lib/pione/front/relay-front.rb +19 -0
  176. data/lib/pione/front/task-worker-front.rb +16 -0
  177. data/lib/pione/front/task-worker-owner.rb +42 -0
  178. data/lib/pione/front/tuple-space-provider-front.rb +22 -0
  179. data/lib/pione/front/tuple-space-provider-owner.rb +11 -0
  180. data/lib/pione/front/tuple-space-receiver-front.rb +18 -0
  181. data/lib/pione/model/assignment.rb +89 -0
  182. data/lib/pione/model/basic-model.rb +395 -0
  183. data/lib/pione/model/binary-operator.rb +80 -0
  184. data/lib/pione/model/block.rb +233 -0
  185. data/lib/pione/model/boolean.rb +138 -0
  186. data/lib/pione/model/call-rule.rb +69 -0
  187. data/lib/pione/model/data-expr.rb +360 -0
  188. data/lib/pione/model/feature-expr.rb +794 -0
  189. data/lib/pione/model/float.rb +107 -0
  190. data/lib/pione/model/integer.rb +140 -0
  191. data/lib/pione/model/list.rb +104 -0
  192. data/lib/pione/model/message.rb +80 -0
  193. data/lib/pione/model/package.rb +48 -0
  194. data/lib/pione/model/parameters.rb +282 -0
  195. data/lib/pione/model/rule-expr.rb +120 -0
  196. data/lib/pione/model/rule-io.rb +166 -0
  197. data/lib/pione/model/rule.rb +294 -0
  198. data/lib/pione/model/string.rb +111 -0
  199. data/lib/pione/model/undefined-value.rb +24 -0
  200. data/lib/pione/model/variable-table.rb +315 -0
  201. data/lib/pione/model/variable.rb +87 -0
  202. data/lib/pione/parser/block-parser.rb +83 -0
  203. data/lib/pione/parser/common-parser.rb +145 -0
  204. data/lib/pione/parser/document-parser.rb +58 -0
  205. data/lib/pione/parser/error-message.yml +4 -0
  206. data/lib/pione/parser/expr-parser.rb +266 -0
  207. data/lib/pione/parser/feature-expr-parser.rb +105 -0
  208. data/lib/pione/parser/flow-element-parser.rb +181 -0
  209. data/lib/pione/parser/literal-parser.rb +182 -0
  210. data/lib/pione/parser/rule-definition-parser.rb +163 -0
  211. data/lib/pione/parser/syntax-error.rb +61 -0
  212. data/lib/pione/patch/array-patch.rb +3 -0
  213. data/lib/pione/patch/drb-patch.rb +467 -0
  214. data/lib/pione/patch/monitor-patch.rb +16 -0
  215. data/lib/pione/patch/rinda-patch.rb +759 -0
  216. data/lib/pione/patch/uri-patch.rb +66 -0
  217. data/lib/pione/relay/receiver-socket.rb +69 -0
  218. data/lib/pione/relay/relay-account-db.rb +55 -0
  219. data/lib/pione/relay/relay-client-db.rb +53 -0
  220. data/lib/pione/relay/relay-socket.rb +215 -0
  221. data/lib/pione/relay/trampoline.rb +22 -0
  222. data/lib/pione/relay/transmitter-socket.rb +167 -0
  223. data/lib/pione/resource/basic-resource.rb +92 -0
  224. data/lib/pione/resource/dropbox-resource.rb +106 -0
  225. data/lib/pione/resource/ftp.rb +84 -0
  226. data/lib/pione/resource/local.rb +113 -0
  227. data/lib/pione/rule-handler/action-handler.rb +184 -0
  228. data/lib/pione/rule-handler/basic-handler.rb +217 -0
  229. data/lib/pione/rule-handler/flow-handler.rb +339 -0
  230. data/lib/pione/rule-handler/root-handler.rb +23 -0
  231. data/lib/pione/rule-handler/system-handler.rb +13 -0
  232. data/lib/pione/system/common.rb +22 -0
  233. data/lib/pione/system/config.rb +20 -0
  234. data/lib/pione/system/document.rb +81 -0
  235. data/lib/pione/system/file-cache.rb +150 -0
  236. data/lib/pione/system/global.rb +346 -0
  237. data/lib/pione/system/identifier.rb +61 -0
  238. data/lib/pione/system/init.rb +16 -0
  239. data/lib/pione/system/object.rb +35 -0
  240. data/lib/pione/transformer/block-transformer.rb +23 -0
  241. data/lib/pione/transformer/document-transformer.rb +36 -0
  242. data/lib/pione/transformer/expr-transformer.rb +89 -0
  243. data/lib/pione/transformer/feature-expr-transformer.rb +56 -0
  244. data/lib/pione/transformer/flow-element-transformer.rb +66 -0
  245. data/lib/pione/transformer/literal-transformer.rb +76 -0
  246. data/lib/pione/transformer/rule-definition-transformer.rb +62 -0
  247. data/lib/pione/transformer/transformer-module.rb +37 -0
  248. data/lib/pione/tuple-space/data-finder.rb +165 -0
  249. data/lib/pione/tuple-space/presence-notifier.rb +83 -0
  250. data/lib/pione/tuple-space/relay.rb +9 -0
  251. data/lib/pione/tuple-space/tuple-space-provider.rb +85 -0
  252. data/lib/pione/tuple-space/tuple-space-receiver.rb +140 -0
  253. data/lib/pione/tuple-space/tuple-space-server-interface.rb +60 -0
  254. data/lib/pione/tuple-space/tuple-space-server.rb +156 -0
  255. data/lib/pione/tuple-space/update-criteria.rb +96 -0
  256. data/lib/pione/tuple/agent-tuple.rb +10 -0
  257. data/lib/pione/tuple/attribute-tuple.rb +7 -0
  258. data/lib/pione/tuple/base-uri-tuple.rb +9 -0
  259. data/lib/pione/tuple/basic-tuple.rb +317 -0
  260. data/lib/pione/tuple/bye-tuple.rb +9 -0
  261. data/lib/pione/tuple/command-tuple.rb +9 -0
  262. data/lib/pione/tuple/data-tuple.rb +18 -0
  263. data/lib/pione/tuple/dry-run-tuple.rb +8 -0
  264. data/lib/pione/tuple/exception-tuple.rb +11 -0
  265. data/lib/pione/tuple/finished-tuple.rb +17 -0
  266. data/lib/pione/tuple/foreground-tuple.rb +7 -0
  267. data/lib/pione/tuple/log-tuple.rb +14 -0
  268. data/lib/pione/tuple/process-info-tuple.rb +9 -0
  269. data/lib/pione/tuple/request-rule-tuple.rb +9 -0
  270. data/lib/pione/tuple/rule-tuple.rb +10 -0
  271. data/lib/pione/tuple/shift-tuple.rb +13 -0
  272. data/lib/pione/tuple/task-tuple.rb +36 -0
  273. data/lib/pione/tuple/task-worker-resource-tuple.rb +9 -0
  274. data/lib/pione/tuple/working-tuple.rb +13 -0
  275. data/lib/pione/uri-scheme/basic-scheme.rb +40 -0
  276. data/lib/pione/uri-scheme/broadcast-scheme.rb +11 -0
  277. data/lib/pione/uri-scheme/dropbox-scheme.rb +9 -0
  278. data/lib/pione/uri-scheme/local-scheme.rb +28 -0
  279. data/lib/pione/util/error-report.rb +12 -0
  280. data/lib/pione/util/log.rb +79 -0
  281. data/lib/pione/util/message.rb +155 -0
  282. data/lib/pione/util/misc.rb +73 -0
  283. data/lib/pione/util/terminal.rb +78 -0
  284. data/lib/pione/util/waiter-table.rb +53 -0
  285. data/lib/pione/version.rb +3 -0
  286. data/misc/env.sh +2 -0
  287. data/misc/test-drb-stop-service.rb +34 -0
  288. data/misc/test-ensure-and-thread-kill.rb +40 -0
  289. data/misc/test-many-waiters-client.rb +56 -0
  290. data/misc/test-many-waiters-server.rb +14 -0
  291. data/misc/write_and_take_test.png +0 -0
  292. data/misc/write_and_take_test.rb +36 -0
  293. data/pione.gemspec +49 -0
  294. data/test/agent/spec_basic-agent.rb +170 -0
  295. data/test/agent/spec_broker.rb +36 -0
  296. data/test/agent/spec_command-listener.rb +30 -0
  297. data/test/agent/spec_input-generator.rb +123 -0
  298. data/test/agent/spec_logger.rb +71 -0
  299. data/test/agent/spec_rule-provider.rb +65 -0
  300. data/test/agent/spec_task-worker.rb +307 -0
  301. data/test/agent/spec_tuple-space-client.rb +30 -0
  302. data/test/model/spec_assignment.rb +51 -0
  303. data/test/model/spec_binary-operator.rb +39 -0
  304. data/test/model/spec_block.rb +154 -0
  305. data/test/model/spec_boolean.rb +115 -0
  306. data/test/model/spec_call-rule.rb +23 -0
  307. data/test/model/spec_data-expr.rb +312 -0
  308. data/test/model/spec_feature-expr.rb +359 -0
  309. data/test/model/spec_feature-expr.yml +16 -0
  310. data/test/model/spec_float.rb +141 -0
  311. data/test/model/spec_integer.rb +141 -0
  312. data/test/model/spec_list.rb +26 -0
  313. data/test/model/spec_message.rb +42 -0
  314. data/test/model/spec_package.rb +15 -0
  315. data/test/model/spec_parameters.rb +148 -0
  316. data/test/model/spec_rule-expr.rb +66 -0
  317. data/test/model/spec_rule-io.rb +32 -0
  318. data/test/model/spec_rule.rb +158 -0
  319. data/test/model/spec_string.rb +106 -0
  320. data/test/model/spec_variable-table.rb +117 -0
  321. data/test/model/spec_variable.rb +84 -0
  322. data/test/parser/spec_block-parser.rb +5 -0
  323. data/test/parser/spec_block-parser.yml +85 -0
  324. data/test/parser/spec_common-parser.rb +281 -0
  325. data/test/parser/spec_expr-parser.rb +6 -0
  326. data/test/parser/spec_expr-parser.yml +82 -0
  327. data/test/parser/spec_feature-expr-parser.rb +32 -0
  328. data/test/parser/spec_feature-expr-parser.yml +25 -0
  329. data/test/parser/spec_flow-element-parser.rb +5 -0
  330. data/test/parser/spec_flow-element-parser.yml +180 -0
  331. data/test/parser/spec_literal-parser.rb +5 -0
  332. data/test/parser/spec_literal-parser.yml +123 -0
  333. data/test/parser/spec_rule-definition-parser.rb +5 -0
  334. data/test/parser/spec_rule-definition-parser.yml +93 -0
  335. data/test/patch/spec_rinda-patch.rb +32 -0
  336. data/test/patch/spec_uri-patch.rb +23 -0
  337. data/test/rule-handler/spec_action-handler.rb +135 -0
  338. data/test/rule-handler/spec_flow-handler.rb +127 -0
  339. data/test/spec_common.rb +14 -0
  340. data/test/spec_data-finder.rb +88 -0
  341. data/test/spec_data-finder.yml +115 -0
  342. data/test/spec_document.rb +76 -0
  343. data/test/spec_identifier.rb +29 -0
  344. data/test/spec_log.rb +52 -0
  345. data/test/spec_object.rb +20 -0
  346. data/test/spec_resource.rb +73 -0
  347. data/test/spec_update-criteria.rb +83 -0
  348. data/test/test-util.rb +223 -0
  349. data/test/transformer/spec_block-transformer.rb +26 -0
  350. data/test/transformer/spec_expr-transformer.rb +106 -0
  351. data/test/transformer/spec_feature-expr-transformer.rb +21 -0
  352. data/test/transformer/spec_flow-element-transformer.rb +154 -0
  353. data/test/transformer/spec_literal-transformer.rb +58 -0
  354. data/test/transformer/spec_rule-definition-transformer.rb +168 -0
  355. data/test/tuple-space/spec_tuple-space-provider.rb +36 -0
  356. data/test/tuple-space/spec_tuple-space-receiver.rb +32 -0
  357. data/test/tuple-space/spec_tuple-space-server.rb +49 -0
  358. data/test/tuple/spec_basic-tuple.rb +87 -0
  359. data/test/tuple/spec_data-tuple.rb +85 -0
  360. data/test/tuple/spec_finished-tuple.rb +61 -0
  361. data/test/tuple/spec_task-tuple.rb +127 -0
  362. data/test/tuple/spec_working-tuple.rb +58 -0
  363. data/test/uri-scheme/spec_broadcast-scheme.rb +40 -0
  364. data/test/uri-scheme/spec_dropbox-scheme.rb +31 -0
  365. data/test/uri-scheme/spec_local-scheme.rb +69 -0
  366. metadata +660 -0
@@ -0,0 +1,16 @@
1
+ module MonitorMixin
2
+ def mon_exit
3
+ # check thread owner but ignore
4
+ begin
5
+ mon_check_owner
6
+ rescue ThreadError => e
7
+ $stderr.puts "Thread owner check error reported but we ignore it."
8
+ ErrorReport.print(e)
9
+ end
10
+ @mon_count -=1
11
+ if @mon_count == 0
12
+ @mon_owner = nil
13
+ @mon_mutex.unlock
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,759 @@
1
+ # @api private
2
+ module Rinda
3
+ class RedundantTupleError < StandardError
4
+ def initialize(tuple)
5
+ @tuple = tuple
6
+ end
7
+
8
+ def message
9
+ "Try to write redundant tuple in tuple space: %s" % @tuple.inspect
10
+ end
11
+
12
+ def inspect
13
+ "#<Rinda::RedundantTupleError @tuple=%s>" % @tuple.inspect
14
+ end
15
+ alias :to_s :inspect
16
+ end
17
+
18
+ class Tuple
19
+ def ==(other)
20
+ return false unless other.kind_of?(Tuple)
21
+ value == other.value
22
+ end
23
+ alias :eql? :"=="
24
+
25
+ def init_with_ary(ary)
26
+ @tuple = Array.new(ary.size)
27
+ # add timestamp
28
+ @tuple.timestamp = ary.timestamp
29
+ @tuple.size.times do |i|
30
+ @tuple[i] = ary[i]
31
+ end
32
+ end
33
+ end
34
+
35
+ class TupleEntry
36
+ def ==(other)
37
+ return false unless other.kind_of?(TupleEntry)
38
+ value == other.value
39
+ end
40
+ alias :eql? :"=="
41
+ end
42
+
43
+ class WaitTemplateEntry
44
+ attr_reader :place
45
+ attr_reader :thread
46
+ attr_reader :found
47
+ attr_accessor :signaled
48
+ attr_accessor :finished
49
+
50
+ # @note
51
+ # removed monitor from original
52
+ def initialize(place, ary, expires=nil)
53
+ super(ary, expires)
54
+ @place = place
55
+ @found = nil
56
+ @signaled = false
57
+ @finished = false
58
+ end
59
+
60
+ # @note
61
+ # thread version(don't use monitor)
62
+ def wait
63
+ @thread = Thread.current
64
+ Thread.stop
65
+ @thread = nil
66
+ end
67
+
68
+ # @note
69
+ # thread version(don't use monitor)
70
+ def signal
71
+ @signaled = true
72
+ if @thread && @thread.status == "sleep"
73
+ @thread.run
74
+ end
75
+ end
76
+
77
+ def inspect
78
+ infos = [
79
+ "@cancel=%s" % @cancel,
80
+ "@expires=%s" % @expires,
81
+ "@tuple=%s" % @tuple.inspect,
82
+ "@renewer=%s" % @renewer.inspect,
83
+ "@found=%s" % @found.inspect,
84
+ "@thread=%s" % @thread.inspect,
85
+ "@signaled=%s" % @signaled,
86
+ "@finished=%s" % @finished
87
+ ]
88
+ "#<%s:%s %s>" % ["Rinda::WaitTemplateEntry", __id__, infos.join(", ")]
89
+ end
90
+ alias :to_s :inspect
91
+
92
+ def ==(other)
93
+ return false unless other.kind_of?(WaitTemplateEntry)
94
+ return false unless value == other.value
95
+ return false unless @thread == other.thread
96
+ return false unless @signaled == other.signaled
97
+ return false unless @finished == other.finished
98
+ return true
99
+ end
100
+ alias :eql? :"=="
101
+ end
102
+
103
+ class TupleBag
104
+ # TupleBin is original array based class.
105
+ class TupleBin
106
+ def elements
107
+ @bin
108
+ end
109
+
110
+ def size
111
+ elements.size
112
+ end
113
+
114
+ def delete(tuple)
115
+ if i = @bin.index(tuple)
116
+ @bin.delete_at(i)
117
+ end
118
+ end
119
+ end
120
+
121
+ # DomainTupleBin is a domain based TupleBin class.
122
+ # @note
123
+ # DomainTupleBin should take tuples that have domain.
124
+ class DomainTupleBin < TupleBin
125
+ # Creates a new bin.
126
+ def initialize
127
+ @bin = {}
128
+ end
129
+
130
+ # Adds the tuple.
131
+ # @param [Array] tuple
132
+ # the tuple
133
+ # @return [void]
134
+ def add(tuple)
135
+ if dom = domain(tuple)
136
+ unless @bin[dom]
137
+ @bin[dom] = tuple
138
+ else
139
+ raise RedundantTupleError.new(tuple.value)
140
+ end
141
+ else
142
+ raise RuntimeError
143
+ end
144
+ end
145
+
146
+ # Deletes the tuple.
147
+ # @param [Array] tuple
148
+ # the tuple
149
+ # @return [void]
150
+ def delete(tuple)
151
+ @bin.delete(domain(tuple))
152
+ end
153
+
154
+ # Deletes tuples that match the block.
155
+ # @yield [Array]
156
+ # each tuple
157
+ # @return [void]
158
+ def delete_if
159
+ return @bin unless block_given?
160
+ @bin.delete_if {|key, val| yield(val)}
161
+ end
162
+
163
+ def elements
164
+ @bin.values
165
+ end
166
+
167
+ # Finds a tuple matched by the template. This method searches by index
168
+ # when the template has the domain, otherwise by liner.
169
+ # @param [TemplateEntry] template
170
+ # template tuple
171
+ # @yield [Array]
172
+ # match condition block
173
+ # @return [Array]
174
+ # a matched tuple
175
+ def find(template, &b)
176
+ if key = domain(template)
177
+ # indexed search
178
+ return @bin[key]
179
+ else
180
+ # liner search
181
+ return @bin.values.find {|val| yield(val)}
182
+ end
183
+ end
184
+
185
+ # Finds all tuples matched by the template. This method searches by index
186
+ # when the template has the domain, otherwise by liner.
187
+ # @param [TemplateEntry] template
188
+ # template tuple
189
+ # @yield [Array]
190
+ # match condition block
191
+ # @return [Array<Array>]
192
+ # matched tuples
193
+ def find_all(template, &b)
194
+ return @bin.values unless block_given?
195
+ if key = domain(template)
196
+ # indexed search
197
+ return [@bin[key]]
198
+ else
199
+ # liner search
200
+ return @bin.select{|_, val| yield(val)}.values
201
+ end
202
+ end
203
+
204
+ # Returns an iterator of the values.
205
+ # @return [Enumerator]
206
+ # iterator of the values
207
+ def each(*args)
208
+ @bin.values.each(*args)
209
+ end
210
+
211
+ private
212
+
213
+ # Returns domain position.
214
+ # @param [Array] tuple
215
+ # the tuple
216
+ # @return [String]
217
+ # the domain
218
+ def domain(tuple)
219
+ identifier = tuple.value[0]
220
+ pos = Pione::Tuple[identifier].domain_position
221
+ tuple.value[pos]
222
+ end
223
+ end
224
+
225
+ # DataTupleBin is a set of domains.
226
+ class DataTupleBin < TupleBin
227
+ def initialize
228
+ @bin = {}
229
+ end
230
+
231
+ def elements
232
+ @bin.values.map{|val| val.values}.flatten
233
+ end
234
+
235
+ # Adds the tuple.
236
+ # @param [Array] tuple
237
+ # the tuple
238
+ # @return [void]
239
+ def add(tuple)
240
+ prepare_table(domain(tuple))
241
+ unless @bin[domain(tuple)][name(tuple)]
242
+ @bin[domain(tuple)][name(tuple)] = tuple
243
+ else
244
+ raise RedundantTupleError.new(tuple.value)
245
+ end
246
+ end
247
+
248
+ def delete(tuple)
249
+ prepare_table(domain(tuple))
250
+ @bin[domain(tuple)].delete(name(tuple))
251
+ end
252
+
253
+ def delete_if
254
+ if block_given?
255
+ @bin.values.each do |table|
256
+ table.delete_if {|_, val| yield(val)}
257
+ end
258
+ end
259
+ return @bin
260
+ end
261
+
262
+ def find(template, &b)
263
+ domain = domain(template)
264
+ name = name(template)
265
+ prepare_table(domain)
266
+ if domain
267
+ @bin[domain].values.each do |tuple|
268
+ return tuple if yield(tuple)
269
+ end
270
+ else
271
+ @bin.values.each do |table|
272
+ table.values.each do |tuple|
273
+ return tuple if yield(tuple)
274
+ end
275
+ end
276
+ end
277
+ return nil
278
+ end
279
+
280
+ def find_all(template, &b)
281
+ domain = domain(template)
282
+ name = name(template)
283
+ prepare_table(domain)
284
+
285
+ if domain
286
+ if block_given?
287
+ return @bin[domain].values.select {|tuple| yield(tuple)}
288
+ else
289
+ return @bin[domain].values
290
+ end
291
+ else
292
+ if block_given?
293
+ return @bin.values.map{|table| table.values}.flatten.select{|tuple|
294
+ yield(tuple)
295
+ }
296
+ else
297
+ return @bin.values.map{|table| table.values}.flatten
298
+ end
299
+ end
300
+ end
301
+
302
+ def each(*args)
303
+ @bin.values.map{|table| table.values}.flatten.each(*args)
304
+ end
305
+
306
+ private
307
+
308
+ def prepare_table(domain)
309
+ if domain
310
+ @bin[domain] = {} unless @bin[domain]
311
+ end
312
+ end
313
+
314
+ # Returns the domain.
315
+ def domain(tuple)
316
+ return tuple.value[1]
317
+ end
318
+
319
+ # Returns the name.
320
+ def name(tuple)
321
+ return tuple.value[2]
322
+ end
323
+ end
324
+
325
+ # HashTupleBin is a hash based bin class.
326
+ class HashTupleBin
327
+ def initialize
328
+ @bin = {}
329
+ end
330
+
331
+ def elements
332
+ @bin.values
333
+ end
334
+
335
+ def add(tuple)
336
+ unless @bin[key(tuple)]
337
+ @bin[key(tuple)] = tuple
338
+ else
339
+ raise RedundantErrorTuple.new(tuple)
340
+ end
341
+ end
342
+
343
+ def delete(tuple)
344
+ @bin.delete(key(tuple))
345
+ end
346
+
347
+ def delete_if
348
+ if block_given?
349
+ @bin.delete_if {|_, val| yield(val)}
350
+ end
351
+ return @bin
352
+ end
353
+
354
+ def find(template, &b)
355
+ if key(template) && @bin.has_key?(key(template))
356
+ tuple = @bin[key(template)]
357
+ return tuple if yield(tuple)
358
+ else
359
+ @bin.values.each do |tuple|
360
+ return tuple if yield(tuple)
361
+ end
362
+ end
363
+ return nil
364
+ end
365
+
366
+ def find_all(template, &b)
367
+ if key(template) && @bin.has_key?(key(template))
368
+ tuple = @bin[key(template)]
369
+ return tuple if yield(tuple)
370
+ else
371
+ return @bin.values.find_all {|tuple|
372
+ yield(tuple)
373
+ }
374
+ end
375
+ end
376
+
377
+ def each(*args)
378
+ @bin.values.each(*args)
379
+ end
380
+
381
+ private
382
+
383
+ # Returns the key.
384
+ def key(tuple)
385
+ # 0:identifier, 1:key, 2:value
386
+ return tuple.value[1]
387
+ end
388
+ end
389
+
390
+ def initialize
391
+ @hash = {}
392
+ @mutex = Mutex.new
393
+ @enum = enum_for(:each_entry)
394
+ @special_bin = {}
395
+ end
396
+
397
+ def [](ident)
398
+ @hash[ident]
399
+ end
400
+
401
+ # Sets special bin class table by identifier.
402
+ def set_special_bin(special_bin)
403
+ @special_bin = special_bin
404
+ end
405
+
406
+ def push(tuple)
407
+ key = bin_key(tuple)
408
+ prepare_table(key)
409
+ @mutex.synchronize {@hash[key].add(tuple)}
410
+ end
411
+
412
+ def delete(tuple)
413
+ key = bin_key(tuple)
414
+ bin = @mutex.synchronize {@hash[key]}
415
+ return nil unless bin
416
+ @mutex.synchronize {bin.delete(tuple)}
417
+ @mutex.synchronize {@hash.delete(key) if bin.empty?}
418
+ return tuple
419
+ end
420
+
421
+ def prepare_table(key)
422
+ unless @mutex.synchronize {@hash[key]}
423
+ @mutex.synchronize {@hash[key] = bin_class(key).new}
424
+ end
425
+ end
426
+
427
+ def bin_class(key)
428
+ return TupleBin unless @special_bin
429
+ return @special_bin.has_key?(key) ? @special_bin[key] : TupleBin
430
+ end
431
+
432
+ alias :orig_find :find
433
+ alias :orig_find_all :find_all
434
+
435
+ public
436
+
437
+ # Returns all tuples in the bag.
438
+ def all_tuples
439
+ @mutex.synchronize{@hash.values}.map{|bin| bin.elements}.flatten
440
+ end
441
+
442
+ def find(template)
443
+ key = bin_key(template)
444
+ if @special_bin[key]
445
+ prepare_table(key)
446
+ @mutex.synchronize{@hash[key]}.find(template) do |tuple|
447
+ tuple.alive? && template.match(tuple)
448
+ end
449
+ else
450
+ orig_find(template)
451
+ end
452
+ end
453
+
454
+ def find_all(template)
455
+ key = bin_key(template)
456
+ if @special_bin[key]
457
+ prepare_table(key)
458
+ @mutex.synchronize{@hash[key]}.find_all(template) do |tuple|
459
+ tuple.alive? && template.match(tuple)
460
+ end
461
+ else
462
+ orig_find_all(template)
463
+ end
464
+ end
465
+
466
+ def find_template(tuple)
467
+ @enum.find do |template|
468
+ template.alive? && template.match(tuple)
469
+ end
470
+ end
471
+
472
+ def delete_unless_alive
473
+ deleted = []
474
+ @mutex.synchronize do
475
+ @hash.each do |key, bin|
476
+ bin.delete_if do |tuple|
477
+ if tuple.alive?
478
+ false
479
+ else
480
+ deleted.push(tuple)
481
+ true
482
+ end
483
+ end
484
+ end
485
+ end
486
+ deleted
487
+ end
488
+
489
+ def task_size
490
+ @mutex.synchronize{@hash[:task]}.size rescue 0
491
+ end
492
+
493
+ def working_size
494
+ @mutex.synchronize{@hash[:working]}.size rescue 0
495
+ end
496
+
497
+ def finished_size
498
+ @mutex.synchronize{@hash[:finished]}.size rescue 0
499
+ end
500
+
501
+ def data_size
502
+ @mutex.synchronize{@hash[:data]}.size rescue 0
503
+ end
504
+
505
+ private
506
+
507
+ def each_entry(&blk)
508
+ @mutex.synchronize do
509
+ @hash.each do |k, v|
510
+ v.each(&blk)
511
+ end
512
+ end
513
+ end
514
+ end
515
+
516
+ class TupleSpace
517
+ attr_reader :bag
518
+ attr_reader :take_waiter
519
+ attr_reader :read_waiter
520
+
521
+ alias :orig_initialize :initialize
522
+
523
+ def initialize(*args)
524
+ orig_initialize(*args)
525
+ @bag.set_special_bin(
526
+ :task => TupleBag::DomainTupleBin,
527
+ :finished => TupleBag::DomainTupleBin,
528
+ :working => TupleBag::DomainTupleBin,
529
+ :data => TupleBag::DataTupleBin,
530
+ :shift => TupleBag::HashTupleBin
531
+ )
532
+ @mutex = Mutex.new
533
+ end
534
+
535
+ def write(tuple, *args)
536
+ tuple.timestamp = Time.now
537
+ real_write(tuple, *args)
538
+ end
539
+
540
+ def move(port, tuple, sec=nil)
541
+ real_move(port, tuple, sec)
542
+ end
543
+
544
+ def read(tuple, sec=nil)
545
+ shift_tuple(real_read(tuple, sec))
546
+ end
547
+
548
+ def read_all(tuple)
549
+ real_read_all(tuple).map do |res|
550
+ shift_tuple(res)
551
+ end
552
+ end
553
+
554
+ # Returns all tuples in the space.
555
+ # @param [Symbol] target
556
+ # tuple type(:all, :bag, :read_waiter, or :take_waiter)
557
+ # @return [Array]
558
+ # all tuples
559
+ def all_tuples(target=:bag)
560
+ case target
561
+ when :all
562
+ all_tuples(:bag) + all_tuples(:read_waiter) + all_tuples(:take_waiter)
563
+ when :bag
564
+ @mutex.synchronize{@bag.all_tuples}.map{|tuple| tuple.value}
565
+ when :read_waiter
566
+ @mutex.synchronize{@read_waiter.all_tuples}.map{|tuple| tuple.value}
567
+ when :take_waiter
568
+ @mutex.synchronize{@take_waiter.all_tuples}.map{|tuple| tuple.value}
569
+ end
570
+ end
571
+
572
+ # @note
573
+ # mutex version of +notify+
574
+ def notify(event, tuple, sec=nil)
575
+ template = NotifyTemplateEntry.new(self, event, tuple, sec)
576
+ @mutex.synchronize {@notify_waiter.push(template)}
577
+ template
578
+ end
579
+
580
+ def task_size
581
+ @bag.task_size
582
+ end
583
+
584
+ def working_size
585
+ @bag.working_size
586
+ end
587
+
588
+ def finished_size
589
+ @bag.finished_size
590
+ end
591
+
592
+ def data_size
593
+ @bag.data_size
594
+ end
595
+
596
+ private
597
+
598
+ # @note
599
+ # mutex version of +write+
600
+ def real_write(tuple, sec=nil)
601
+ entry = create_entry(tuple, sec)
602
+ if entry.expired?
603
+ # why only read_waiter???
604
+ @mutex.synchronize{@read_waiter.find_all_template(entry)}.each do |template|
605
+ template.read(tuple)
606
+ end
607
+ notify_event('write', entry.value)
608
+ notify_event('delete', entry.value)
609
+ else
610
+ # push to bag
611
+ @mutex.synchronize do
612
+ @bag.push(entry)
613
+ end
614
+ # start keeper
615
+ start_keeper if entry.expires
616
+ # send tuple to all matched waiters in read waiter list
617
+ @mutex.synchronize do
618
+ @read_waiter.find_all_template(entry).each do |template|
619
+ template.read(tuple)
620
+ end
621
+ end
622
+ # send tuple to one of matched waiters in take waiter list
623
+ @mutex.synchronize do
624
+ if template = @take_waiter.find_template(entry)
625
+ template.signal
626
+ end
627
+ end
628
+ notify_event('write', entry.value)
629
+ end
630
+ entry
631
+ end
632
+
633
+ # @note
634
+ # mutex version of +move+
635
+ def real_move(port, tuple, sec=nil)
636
+ template = WaitTemplateEntry.new(self, tuple, sec)
637
+ yield(template) if block_given?
638
+
639
+ if entry = @mutex.synchronize {@bag.find(template)}
640
+ port.push(entry.value) if port
641
+ @mutex.synchronize {@bag.delete(entry)}
642
+ notify_event('take', entry.value)
643
+ template.finished = true
644
+ return entry.value
645
+ end
646
+ raise RequestExpiredError if template.expired?
647
+
648
+ begin
649
+ @mutex.synchronize {@take_waiter.push(template)}
650
+ start_keeper if template.expires
651
+ while true
652
+ raise RequestCanceledError if template.canceled?
653
+ raise RequestExpiredError if template.expired?
654
+ if entry = @mutex.synchronize {@bag.find(template)}
655
+ port.push(entry.value) if port
656
+ @mutex.synchronize {@bag.delete(entry)}
657
+ notify_event('take', entry.value)
658
+ template.finished = true
659
+ @mutex.synchronize do
660
+ @take_waiter.delete(template)
661
+ end
662
+ return entry.value
663
+ end
664
+ Thread.current[:WaitTemplate] = template
665
+ template.wait
666
+ Thread.current[:WaitTemplate] = nil
667
+ end
668
+ ensure
669
+ @mutex.synchronize {@take_waiter.delete(template)}
670
+ end
671
+ end
672
+
673
+ # @note
674
+ # mutex version of +read+
675
+ def real_read(tuple, sec=nil)
676
+ template = WaitTemplateEntry.new(self, tuple, sec)
677
+ yield(template) if block_given?
678
+
679
+ entry = @mutex.synchronize {@bag.find(template)}
680
+ return entry.value if entry
681
+ raise RequestExpiredError if template.expired?
682
+
683
+ begin
684
+ @mutex.synchronize {@read_waiter.push(template)}
685
+ start_keeper if template.expires
686
+ template.wait
687
+ raise RequestCanceledError if template.canceled?
688
+ raise RequestExpiredError if template.expired?
689
+ return template.found
690
+ ensure
691
+ @mutex.synchronize {@read_waiter.delete(template)}
692
+ end
693
+ end
694
+
695
+ # @note
696
+ # mutex version of +read_all+
697
+ def real_read_all(tuple)
698
+ template = WaitTemplateEntry.new(self, tuple, nil)
699
+ entry = @mutex.synchronize {@bag.find_all(template)}
700
+ entry.collect {|e| e.value}
701
+ end
702
+
703
+ # @note
704
+ # mutex version of +keep_clean+
705
+ def keep_clean
706
+ @mutex.synchronize{@read_waiter.delete_unless_alive}.each do |e|
707
+ e.signal
708
+ end
709
+ @mutex.synchronize{@take_waiter.delete_unless_alive}.each do |e|
710
+ e.signal
711
+ end
712
+ @mutex.synchronize{@notify_waiter.delete_unless_alive}.each do |e|
713
+ e.notify(['close'])
714
+ end
715
+ @mutex.synchronize{@bag.delete_unless_alive}.each do |e|
716
+ notify_event('delete', e.value)
717
+ end
718
+ end
719
+
720
+ # @note
721
+ # mutex version of +start_keeper+
722
+ def start_keeper
723
+ return if @keeper && @keeper.alive?
724
+ @keeper = Thread.new do
725
+ while true
726
+ sleep(@period)
727
+ break unless need_keeper?
728
+ keep_clean
729
+ end
730
+ end
731
+ end
732
+
733
+ def shift_tuple(tuple)
734
+ if Pione::Tuple[tuple.first]
735
+ if pos = Pione::Tuple[tuple.first].uri_position
736
+ if new_uri = shift_uri(tuple[pos])
737
+ tuple = tuple.clone
738
+ tuple[pos] = new_uri
739
+ end
740
+ end
741
+ end
742
+ return tuple
743
+ end
744
+
745
+ def shift_uri(uri, old=[])
746
+ return nil if old.include?(uri)
747
+
748
+ template = TemplateEntry.new([:shift, uri, nil])
749
+ if shift_tuple = @bag.find(template)
750
+ next_uri = shift_tuple[2]
751
+ if last_uri = shift_uri(next_uri, old + [uri])
752
+ next_uri = last_uri
753
+ end
754
+ return next_uri
755
+ end
756
+ return nil
757
+ end
758
+ end
759
+ end