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.
- data/.gitignore +14 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +94 -0
- data/Rakefile +118 -0
- data/bin/pione-broker +5 -0
- data/bin/pione-clean +5 -0
- data/bin/pione-client +5 -0
- data/bin/pione-eval +111 -0
- data/bin/pione-relay +5 -0
- data/bin/pione-relay-account-db +5 -0
- data/bin/pione-relay-client-db +5 -0
- data/bin/pione-search-log +30 -0
- data/bin/pione-syntax-checker +5 -0
- data/bin/pione-task-worker +5 -0
- data/bin/pione-tuple-space-provider +5 -0
- data/bin/pione-tuple-space-receiver +5 -0
- data/bin/pione-tuple-space-viewer +5 -0
- data/demo/demo.rb +311 -0
- data/demo/public/base.css +94 -0
- data/demo/public/demo.js +107 -0
- data/demo/public/index.html +91 -0
- data/demo/public/jquery-1.8.3.min.js +2 -0
- data/example/CountChar/CountChar.pione +64 -0
- data/example/CountChar/misc/CountChar.rb +22 -0
- data/example/CountChar/text/aidokushono_insho.txt +32 -0
- data/example/CountChar/text/aikokuka_shokan.txt +108 -0
- data/example/CountChar/text/carlyle_hakubutsukan.txt +58 -0
- data/example/CountChar/text/dark_minister.txt +2440 -0
- data/example/CountChar/text/kaikano_otto.txt +61 -0
- data/example/CountChar/text/kaikon.txt +30 -0
- data/example/CountChar/text/nagashimano_shi.txt +45 -0
- data/example/CountChar/text/saikachibuchi.txt +80 -0
- data/example/CountChar/text/saikonihonno_josei.txt +91 -0
- data/example/CountChar/text/taishojugonenno_bundan.txt +21 -0
- data/example/FeatureExample/FeatureExample.pione +7 -0
- data/example/Fib/Fib.pione +56 -0
- data/example/Fib/FibBC.pione +56 -0
- data/example/HelloWorld/HelloWorld.pione +5 -0
- data/example/LucasNumber/LucasNumber.pione +64 -0
- data/example/MakePair/MakePair.pione +14 -0
- data/example/MakePair/data/1.i +0 -0
- data/example/MakePair/data/2.i +0 -0
- data/example/MakePair/data/3.i +0 -0
- data/example/MakePair/data/4.i +0 -0
- data/example/MakePair/data/5.i +0 -0
- data/example/SieveOfEratosthenes/SieveOfEratosthenes.pione +61 -0
- data/example/SingleParticlesWithRef/Makefile +289 -0
- data/example/SingleParticlesWithRef/SingleParticlesWithRef.Makefile +153 -0
- data/example/SingleParticlesWithRef/SingleParticlesWithRef.pione +116 -0
- data/example/SingleParticlesWithRef/SingleParticlesWithRefFull.pione +400 -0
- data/example/SingleParticlesWithRef/data/121p-shift-0-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-0-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-0-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-0-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-0-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-0-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-120-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-120-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-120-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-120-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-120-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-120-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-180-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-180-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-180-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-180-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-180-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-180-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-240-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-240-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-240-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-240-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-240-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-240-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-300-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-300-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-300-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-300-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-300-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-300-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-60-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-60-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-60-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-60-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-60-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-60-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-0-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-0-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-0-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-0-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-0-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-0-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-120-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-120-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-120-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-120-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-120-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-120-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-180-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-180-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-180-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-180-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-180-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-180-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-240-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-240-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-240-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-240-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-240-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-240-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-300-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-300-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-300-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-300-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-300-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-300-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-60-0-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-60-120-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-60-180-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-60-240-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-60-300-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift-noise-60-60-0.roi +0 -0
- data/example/SingleParticlesWithRef/data/121p-shift.pdb +3381 -0
- data/example/SingleParticlesWithRef/data/all.ref2d +0 -0
- data/example/SingleParticlesWithRef/data/all.ref3d +0 -0
- data/example/Sum/Sum.pione +52 -0
- data/example/SyntaxError/call_rule_error.pione +6 -0
- data/example/SyntaxError/feature_line_error.pione +7 -0
- data/example/SyntaxError/flow_block_error.pione +5 -0
- data/example/SyntaxError/input_line_error.pione +6 -0
- data/example/SyntaxError/invalid_rule_name.pione +6 -0
- data/example/SyntaxError/param_line_error.pione +7 -0
- data/example/SyntaxError/variable-binding-error.pione +11 -0
- data/lib/pione.rb +241 -0
- data/lib/pione/agent/basic-agent.rb +333 -0
- data/lib/pione/agent/broker.rb +274 -0
- data/lib/pione/agent/command-listener.rb +47 -0
- data/lib/pione/agent/input-generator.rb +194 -0
- data/lib/pione/agent/logger.rb +65 -0
- data/lib/pione/agent/process-manager.rb +38 -0
- data/lib/pione/agent/rule-provider.rb +64 -0
- data/lib/pione/agent/task-worker.rb +274 -0
- data/lib/pione/agent/trivial-routine-worker.rb +28 -0
- data/lib/pione/agent/tuple-space-client.rb +146 -0
- data/lib/pione/agent/tuple-space-server-client-life-checker.rb +29 -0
- data/lib/pione/command-option/basic-option.rb +42 -0
- data/lib/pione/command-option/child-process-option.rb +17 -0
- data/lib/pione/command-option/common-option.rb +29 -0
- data/lib/pione/command-option/daemon-option.rb +12 -0
- data/lib/pione/command-option/presence-notifier-option.rb +15 -0
- data/lib/pione/command-option/task-worker-owner-option.rb +17 -0
- data/lib/pione/command-option/tuple-space-provider-option.rb +26 -0
- data/lib/pione/command-option/tuple-space-provider-owner-option.rb +16 -0
- data/lib/pione/command-option/tuple-space-receiver-option.rb +12 -0
- data/lib/pione/command/basic-command.rb +126 -0
- data/lib/pione/command/child-process.rb +43 -0
- data/lib/pione/command/daemon-process.rb +18 -0
- data/lib/pione/command/front-owner-command.rb +37 -0
- data/lib/pione/command/pione-broker.rb +53 -0
- data/lib/pione/command/pione-clean.rb +16 -0
- data/lib/pione/command/pione-client.rb +273 -0
- data/lib/pione/command/pione-relay-account-db.rb +85 -0
- data/lib/pione/command/pione-relay-client-db.rb +80 -0
- data/lib/pione/command/pione-relay.rb +47 -0
- data/lib/pione/command/pione-syntax-checker.rb +103 -0
- data/lib/pione/command/pione-task-worker.rb +123 -0
- data/lib/pione/command/pione-tuple-space-provider.rb +87 -0
- data/lib/pione/command/pione-tuple-space-receiver.rb +55 -0
- data/lib/pione/command/pione-tuple-space-viewer.rb +151 -0
- data/lib/pione/command/tuple-space-provider-owner.rb +6 -0
- data/lib/pione/front/basic-front.rb +64 -0
- data/lib/pione/front/broker-front.rb +30 -0
- data/lib/pione/front/client-front.rb +28 -0
- data/lib/pione/front/relay-front.rb +19 -0
- data/lib/pione/front/task-worker-front.rb +16 -0
- data/lib/pione/front/task-worker-owner.rb +42 -0
- data/lib/pione/front/tuple-space-provider-front.rb +22 -0
- data/lib/pione/front/tuple-space-provider-owner.rb +11 -0
- data/lib/pione/front/tuple-space-receiver-front.rb +18 -0
- data/lib/pione/model/assignment.rb +89 -0
- data/lib/pione/model/basic-model.rb +395 -0
- data/lib/pione/model/binary-operator.rb +80 -0
- data/lib/pione/model/block.rb +233 -0
- data/lib/pione/model/boolean.rb +138 -0
- data/lib/pione/model/call-rule.rb +69 -0
- data/lib/pione/model/data-expr.rb +360 -0
- data/lib/pione/model/feature-expr.rb +794 -0
- data/lib/pione/model/float.rb +107 -0
- data/lib/pione/model/integer.rb +140 -0
- data/lib/pione/model/list.rb +104 -0
- data/lib/pione/model/message.rb +80 -0
- data/lib/pione/model/package.rb +48 -0
- data/lib/pione/model/parameters.rb +282 -0
- data/lib/pione/model/rule-expr.rb +120 -0
- data/lib/pione/model/rule-io.rb +166 -0
- data/lib/pione/model/rule.rb +294 -0
- data/lib/pione/model/string.rb +111 -0
- data/lib/pione/model/undefined-value.rb +24 -0
- data/lib/pione/model/variable-table.rb +315 -0
- data/lib/pione/model/variable.rb +87 -0
- data/lib/pione/parser/block-parser.rb +83 -0
- data/lib/pione/parser/common-parser.rb +145 -0
- data/lib/pione/parser/document-parser.rb +58 -0
- data/lib/pione/parser/error-message.yml +4 -0
- data/lib/pione/parser/expr-parser.rb +266 -0
- data/lib/pione/parser/feature-expr-parser.rb +105 -0
- data/lib/pione/parser/flow-element-parser.rb +181 -0
- data/lib/pione/parser/literal-parser.rb +182 -0
- data/lib/pione/parser/rule-definition-parser.rb +163 -0
- data/lib/pione/parser/syntax-error.rb +61 -0
- data/lib/pione/patch/array-patch.rb +3 -0
- data/lib/pione/patch/drb-patch.rb +467 -0
- data/lib/pione/patch/monitor-patch.rb +16 -0
- data/lib/pione/patch/rinda-patch.rb +759 -0
- data/lib/pione/patch/uri-patch.rb +66 -0
- data/lib/pione/relay/receiver-socket.rb +69 -0
- data/lib/pione/relay/relay-account-db.rb +55 -0
- data/lib/pione/relay/relay-client-db.rb +53 -0
- data/lib/pione/relay/relay-socket.rb +215 -0
- data/lib/pione/relay/trampoline.rb +22 -0
- data/lib/pione/relay/transmitter-socket.rb +167 -0
- data/lib/pione/resource/basic-resource.rb +92 -0
- data/lib/pione/resource/dropbox-resource.rb +106 -0
- data/lib/pione/resource/ftp.rb +84 -0
- data/lib/pione/resource/local.rb +113 -0
- data/lib/pione/rule-handler/action-handler.rb +184 -0
- data/lib/pione/rule-handler/basic-handler.rb +217 -0
- data/lib/pione/rule-handler/flow-handler.rb +339 -0
- data/lib/pione/rule-handler/root-handler.rb +23 -0
- data/lib/pione/rule-handler/system-handler.rb +13 -0
- data/lib/pione/system/common.rb +22 -0
- data/lib/pione/system/config.rb +20 -0
- data/lib/pione/system/document.rb +81 -0
- data/lib/pione/system/file-cache.rb +150 -0
- data/lib/pione/system/global.rb +346 -0
- data/lib/pione/system/identifier.rb +61 -0
- data/lib/pione/system/init.rb +16 -0
- data/lib/pione/system/object.rb +35 -0
- data/lib/pione/transformer/block-transformer.rb +23 -0
- data/lib/pione/transformer/document-transformer.rb +36 -0
- data/lib/pione/transformer/expr-transformer.rb +89 -0
- data/lib/pione/transformer/feature-expr-transformer.rb +56 -0
- data/lib/pione/transformer/flow-element-transformer.rb +66 -0
- data/lib/pione/transformer/literal-transformer.rb +76 -0
- data/lib/pione/transformer/rule-definition-transformer.rb +62 -0
- data/lib/pione/transformer/transformer-module.rb +37 -0
- data/lib/pione/tuple-space/data-finder.rb +165 -0
- data/lib/pione/tuple-space/presence-notifier.rb +83 -0
- data/lib/pione/tuple-space/relay.rb +9 -0
- data/lib/pione/tuple-space/tuple-space-provider.rb +85 -0
- data/lib/pione/tuple-space/tuple-space-receiver.rb +140 -0
- data/lib/pione/tuple-space/tuple-space-server-interface.rb +60 -0
- data/lib/pione/tuple-space/tuple-space-server.rb +156 -0
- data/lib/pione/tuple-space/update-criteria.rb +96 -0
- data/lib/pione/tuple/agent-tuple.rb +10 -0
- data/lib/pione/tuple/attribute-tuple.rb +7 -0
- data/lib/pione/tuple/base-uri-tuple.rb +9 -0
- data/lib/pione/tuple/basic-tuple.rb +317 -0
- data/lib/pione/tuple/bye-tuple.rb +9 -0
- data/lib/pione/tuple/command-tuple.rb +9 -0
- data/lib/pione/tuple/data-tuple.rb +18 -0
- data/lib/pione/tuple/dry-run-tuple.rb +8 -0
- data/lib/pione/tuple/exception-tuple.rb +11 -0
- data/lib/pione/tuple/finished-tuple.rb +17 -0
- data/lib/pione/tuple/foreground-tuple.rb +7 -0
- data/lib/pione/tuple/log-tuple.rb +14 -0
- data/lib/pione/tuple/process-info-tuple.rb +9 -0
- data/lib/pione/tuple/request-rule-tuple.rb +9 -0
- data/lib/pione/tuple/rule-tuple.rb +10 -0
- data/lib/pione/tuple/shift-tuple.rb +13 -0
- data/lib/pione/tuple/task-tuple.rb +36 -0
- data/lib/pione/tuple/task-worker-resource-tuple.rb +9 -0
- data/lib/pione/tuple/working-tuple.rb +13 -0
- data/lib/pione/uri-scheme/basic-scheme.rb +40 -0
- data/lib/pione/uri-scheme/broadcast-scheme.rb +11 -0
- data/lib/pione/uri-scheme/dropbox-scheme.rb +9 -0
- data/lib/pione/uri-scheme/local-scheme.rb +28 -0
- data/lib/pione/util/error-report.rb +12 -0
- data/lib/pione/util/log.rb +79 -0
- data/lib/pione/util/message.rb +155 -0
- data/lib/pione/util/misc.rb +73 -0
- data/lib/pione/util/terminal.rb +78 -0
- data/lib/pione/util/waiter-table.rb +53 -0
- data/lib/pione/version.rb +3 -0
- data/misc/env.sh +2 -0
- data/misc/test-drb-stop-service.rb +34 -0
- data/misc/test-ensure-and-thread-kill.rb +40 -0
- data/misc/test-many-waiters-client.rb +56 -0
- data/misc/test-many-waiters-server.rb +14 -0
- data/misc/write_and_take_test.png +0 -0
- data/misc/write_and_take_test.rb +36 -0
- data/pione.gemspec +49 -0
- data/test/agent/spec_basic-agent.rb +170 -0
- data/test/agent/spec_broker.rb +36 -0
- data/test/agent/spec_command-listener.rb +30 -0
- data/test/agent/spec_input-generator.rb +123 -0
- data/test/agent/spec_logger.rb +71 -0
- data/test/agent/spec_rule-provider.rb +65 -0
- data/test/agent/spec_task-worker.rb +307 -0
- data/test/agent/spec_tuple-space-client.rb +30 -0
- data/test/model/spec_assignment.rb +51 -0
- data/test/model/spec_binary-operator.rb +39 -0
- data/test/model/spec_block.rb +154 -0
- data/test/model/spec_boolean.rb +115 -0
- data/test/model/spec_call-rule.rb +23 -0
- data/test/model/spec_data-expr.rb +312 -0
- data/test/model/spec_feature-expr.rb +359 -0
- data/test/model/spec_feature-expr.yml +16 -0
- data/test/model/spec_float.rb +141 -0
- data/test/model/spec_integer.rb +141 -0
- data/test/model/spec_list.rb +26 -0
- data/test/model/spec_message.rb +42 -0
- data/test/model/spec_package.rb +15 -0
- data/test/model/spec_parameters.rb +148 -0
- data/test/model/spec_rule-expr.rb +66 -0
- data/test/model/spec_rule-io.rb +32 -0
- data/test/model/spec_rule.rb +158 -0
- data/test/model/spec_string.rb +106 -0
- data/test/model/spec_variable-table.rb +117 -0
- data/test/model/spec_variable.rb +84 -0
- data/test/parser/spec_block-parser.rb +5 -0
- data/test/parser/spec_block-parser.yml +85 -0
- data/test/parser/spec_common-parser.rb +281 -0
- data/test/parser/spec_expr-parser.rb +6 -0
- data/test/parser/spec_expr-parser.yml +82 -0
- data/test/parser/spec_feature-expr-parser.rb +32 -0
- data/test/parser/spec_feature-expr-parser.yml +25 -0
- data/test/parser/spec_flow-element-parser.rb +5 -0
- data/test/parser/spec_flow-element-parser.yml +180 -0
- data/test/parser/spec_literal-parser.rb +5 -0
- data/test/parser/spec_literal-parser.yml +123 -0
- data/test/parser/spec_rule-definition-parser.rb +5 -0
- data/test/parser/spec_rule-definition-parser.yml +93 -0
- data/test/patch/spec_rinda-patch.rb +32 -0
- data/test/patch/spec_uri-patch.rb +23 -0
- data/test/rule-handler/spec_action-handler.rb +135 -0
- data/test/rule-handler/spec_flow-handler.rb +127 -0
- data/test/spec_common.rb +14 -0
- data/test/spec_data-finder.rb +88 -0
- data/test/spec_data-finder.yml +115 -0
- data/test/spec_document.rb +76 -0
- data/test/spec_identifier.rb +29 -0
- data/test/spec_log.rb +52 -0
- data/test/spec_object.rb +20 -0
- data/test/spec_resource.rb +73 -0
- data/test/spec_update-criteria.rb +83 -0
- data/test/test-util.rb +223 -0
- data/test/transformer/spec_block-transformer.rb +26 -0
- data/test/transformer/spec_expr-transformer.rb +106 -0
- data/test/transformer/spec_feature-expr-transformer.rb +21 -0
- data/test/transformer/spec_flow-element-transformer.rb +154 -0
- data/test/transformer/spec_literal-transformer.rb +58 -0
- data/test/transformer/spec_rule-definition-transformer.rb +168 -0
- data/test/tuple-space/spec_tuple-space-provider.rb +36 -0
- data/test/tuple-space/spec_tuple-space-receiver.rb +32 -0
- data/test/tuple-space/spec_tuple-space-server.rb +49 -0
- data/test/tuple/spec_basic-tuple.rb +87 -0
- data/test/tuple/spec_data-tuple.rb +85 -0
- data/test/tuple/spec_finished-tuple.rb +61 -0
- data/test/tuple/spec_task-tuple.rb +127 -0
- data/test/tuple/spec_working-tuple.rb +58 -0
- data/test/uri-scheme/spec_broadcast-scheme.rb +40 -0
- data/test/uri-scheme/spec_dropbox-scheme.rb +31 -0
- data/test/uri-scheme/spec_local-scheme.rb +69 -0
- metadata +660 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module Pione
|
|
2
|
+
module Resource
|
|
3
|
+
# Local represents local path resources.
|
|
4
|
+
class Local < BasicResource
|
|
5
|
+
# Creates a local resource handler with URI.
|
|
6
|
+
# @param [String, URI] uri
|
|
7
|
+
# URI of a local path
|
|
8
|
+
def initialize(uri)
|
|
9
|
+
@uri = uri.kind_of?(::URI::Generic) ? uri : ::URI.parse(uri)
|
|
10
|
+
raise ArgumentError unless @uri.kind_of?(URIScheme::LocalScheme)
|
|
11
|
+
@path = Pathname.new(uri.path)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# (see BasicResource#create)
|
|
15
|
+
def create(data)
|
|
16
|
+
@path.dirname.mkpath unless @path.dirname.exist?
|
|
17
|
+
@path.open("w+"){|file| file.write(data)}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# (see BasicResource#read)
|
|
21
|
+
def read
|
|
22
|
+
@path.exist? ? @path.read : (raise NotFound.new(@uri))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# (see BasicResource#update)
|
|
26
|
+
def update(data)
|
|
27
|
+
if @path.exist?
|
|
28
|
+
@path.open("w+"){|file| file.write(data)}
|
|
29
|
+
else
|
|
30
|
+
raise NotFound.new(@uri)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# (see BasicResource#delete)
|
|
35
|
+
def delete
|
|
36
|
+
@path.delete if @path.exist?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# (see BasicResource#mtime)
|
|
40
|
+
def mtime
|
|
41
|
+
@path.mtime
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# (see BasicResource#entries)
|
|
45
|
+
def entries
|
|
46
|
+
@path.entries.select{|entry| (@path + entry).file?}.map do |entry|
|
|
47
|
+
Resource[::URI.parse("local:%s" % (@path + entry).expand_path)]
|
|
48
|
+
end
|
|
49
|
+
rescue Errno::ENOENT
|
|
50
|
+
raise NotFound.new(self)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# (see BasicResource#basename)
|
|
54
|
+
def basename
|
|
55
|
+
@path.basename.to_s
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# (see BasicResource#exist?)
|
|
59
|
+
def exist?
|
|
60
|
+
@path.exist?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Makes symbolic link from the resource to the destination.
|
|
64
|
+
# @param [String] dest
|
|
65
|
+
# destination path
|
|
66
|
+
# @return [void]
|
|
67
|
+
def link_to(dest)
|
|
68
|
+
dir = File.dirname(dest)
|
|
69
|
+
FileUtils.makedirs(dir) unless Dir.exist?(dir)
|
|
70
|
+
FileUtils.symlink(@path, dest)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Moves the source to the resource and makes reverse link.
|
|
74
|
+
# @param [String] src
|
|
75
|
+
# source path
|
|
76
|
+
# @return [void]
|
|
77
|
+
def link_from(src)
|
|
78
|
+
swap(src)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Swaps the source file and the resource file. This method moves the
|
|
82
|
+
# other file to the resource path and creates a symbolic link from
|
|
83
|
+
# distination to source.
|
|
84
|
+
# @param [String] other
|
|
85
|
+
# swap target
|
|
86
|
+
# @return [void]
|
|
87
|
+
def swap(other)
|
|
88
|
+
unless File.ftype(other) == "file"
|
|
89
|
+
raise ArgumentError.new(other)
|
|
90
|
+
end
|
|
91
|
+
dir = @path.dirname
|
|
92
|
+
dir.mkpath unless dir.exist?
|
|
93
|
+
FileUtils.mv(other, @path)
|
|
94
|
+
FileUtils.symlink(@path, other)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Swaps the resouce file and the other resouce file.
|
|
98
|
+
# @param [Local] other
|
|
99
|
+
# swap target
|
|
100
|
+
def shift_from(other)
|
|
101
|
+
raise ArgumentError.new(other) unless other.kind_of?(self.class)
|
|
102
|
+
unless File.ftype(other.path) == "file"
|
|
103
|
+
raise ArgumentError.new(other)
|
|
104
|
+
end
|
|
105
|
+
dir = @path.dirname
|
|
106
|
+
dir.mkpath unless dir.exist?
|
|
107
|
+
FileUtils.mv(other.path, @path)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
@@schemes['local'] = Local
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
module Pione
|
|
2
|
+
module RuleHandler
|
|
3
|
+
# ActionHandler handles ActionRule.
|
|
4
|
+
class ActionHandler < BasicHandler
|
|
5
|
+
def self.message_name
|
|
6
|
+
"Action"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :working_directory
|
|
10
|
+
|
|
11
|
+
def initialize(*args)
|
|
12
|
+
super(*args)
|
|
13
|
+
@working_directory = make_working_directory
|
|
14
|
+
setup_variable_table
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Execute the action.
|
|
18
|
+
def execute
|
|
19
|
+
# prepare input files
|
|
20
|
+
setup_working_directory
|
|
21
|
+
# prepare shell script
|
|
22
|
+
write_shell_script {|path| call_shell_script(path) }
|
|
23
|
+
# collect outputs
|
|
24
|
+
collect_outputs
|
|
25
|
+
# write resouces
|
|
26
|
+
write_output_resources
|
|
27
|
+
# write tuples
|
|
28
|
+
write_output_tuples
|
|
29
|
+
# write environment info
|
|
30
|
+
write_env_info
|
|
31
|
+
# write other resources
|
|
32
|
+
write_other_resources
|
|
33
|
+
|
|
34
|
+
# return tuples
|
|
35
|
+
return @outputs
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Setup the variable table with working directory in addition.
|
|
41
|
+
def setup_variable_table
|
|
42
|
+
unless @working_directory
|
|
43
|
+
super
|
|
44
|
+
else
|
|
45
|
+
@variable_table.set(
|
|
46
|
+
Variable.new("WORKING_DIRECTORY"),
|
|
47
|
+
PioneString.new(@working_directory)
|
|
48
|
+
)
|
|
49
|
+
@variable_table.set(
|
|
50
|
+
Variable.new("PWD"),
|
|
51
|
+
PioneString.new(@working_directory)
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Make a working directory.
|
|
57
|
+
def make_working_directory
|
|
58
|
+
# build directory path
|
|
59
|
+
task_dirname = ID.domain_id(
|
|
60
|
+
@rule.expr.package.name,
|
|
61
|
+
@rule.expr.name,
|
|
62
|
+
@inputs,
|
|
63
|
+
@original_params
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# create a directory
|
|
67
|
+
path = Global.working_directory + task_dirname
|
|
68
|
+
FileUtils.makedirs(path)
|
|
69
|
+
|
|
70
|
+
return path
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Synchronize input data into working directory.
|
|
74
|
+
def setup_working_directory
|
|
75
|
+
@inputs.flatten.each do |input|
|
|
76
|
+
# get file path in working directory
|
|
77
|
+
path = File.join(@working_directory, input.name)
|
|
78
|
+
# create a link to cache
|
|
79
|
+
cache_path = FileCache.get(input.uri)
|
|
80
|
+
FileUtils.symlink(cache_path, path, {:force => true})
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Write the data to the tempfile as shell script.
|
|
85
|
+
def write_shell_script(&b)
|
|
86
|
+
file = File.open(
|
|
87
|
+
File.join(@working_directory,"__pione-action__.sh"),
|
|
88
|
+
"w+"
|
|
89
|
+
)
|
|
90
|
+
if @dry_run
|
|
91
|
+
@rule.outputs.flatten.each do |output|
|
|
92
|
+
file.puts("touch %s" % output.eval(@variable_table).name)
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
file.print(@rule.body.eval(@variable_table).content)
|
|
96
|
+
end
|
|
97
|
+
debug_message("Action #{file.path}")
|
|
98
|
+
user_message("-"*60, 0, "SH")
|
|
99
|
+
@rule.body.eval(@variable_table).content.split("\n").each do |line|
|
|
100
|
+
user_message(line, 0, "SH")
|
|
101
|
+
end
|
|
102
|
+
user_message("-"*60, 0, "SH")
|
|
103
|
+
file.close
|
|
104
|
+
FileUtils.chmod(0700, file.path)
|
|
105
|
+
return b.call(file.path)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Call shell script of the path.
|
|
109
|
+
def call_shell_script(path)
|
|
110
|
+
scriptname = File.basename(path)
|
|
111
|
+
|
|
112
|
+
# stdout & stderr
|
|
113
|
+
stdout = @rule.outputs.map {|output|
|
|
114
|
+
output.eval(@variable_table)
|
|
115
|
+
}.find {|output| output.stdout?}
|
|
116
|
+
out = stdout ? stdout.name : ".stdout"
|
|
117
|
+
err = ".stderr"
|
|
118
|
+
|
|
119
|
+
# execute command
|
|
120
|
+
`cd #{@working_directory}; ./#{scriptname} > #{out} 2> #{err}`
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Make output tuple by name.
|
|
124
|
+
def make_output_tuple_with_time(name)
|
|
125
|
+
time = File.mtime(File.join(@working_directory, name))
|
|
126
|
+
uri = make_output_resource_uri(name).to_s
|
|
127
|
+
Tuple[:data].new(name: name, domain: @domain, uri: uri, time: time)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Collect output data by names from working directory.
|
|
131
|
+
def collect_outputs
|
|
132
|
+
list = Dir.entries(@working_directory)
|
|
133
|
+
@rule.outputs.each_with_index do |output, i|
|
|
134
|
+
output = output.eval(@variable_table)
|
|
135
|
+
case output.modifier
|
|
136
|
+
when :all
|
|
137
|
+
@outputs[i] = list.select{|name| output.match(name)}.map do |name|
|
|
138
|
+
make_output_tuple_with_time(name)
|
|
139
|
+
end
|
|
140
|
+
when :each
|
|
141
|
+
if name = list.find {|name| output.match(name)}
|
|
142
|
+
@outputs[i] = make_output_tuple_with_time(name)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Write resources for output data.
|
|
149
|
+
def write_output_resources
|
|
150
|
+
@outputs.flatten.compact.each do |output|
|
|
151
|
+
path = File.join(@working_directory, output.name)
|
|
152
|
+
FileCache.put(path, output.uri)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Writes action environment information file.
|
|
157
|
+
def write_env_info
|
|
158
|
+
path = File.join(@working_directory, ".pione-env")
|
|
159
|
+
File.open(path, "w+") do |out|
|
|
160
|
+
@variable_table.variables.each do |var|
|
|
161
|
+
val = @variable_table.get(var)
|
|
162
|
+
out.puts "%s: %s" % [var.name, val.textize]
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Writes resources for other intermediate files.
|
|
168
|
+
def write_other_resources
|
|
169
|
+
Dir.new(@working_directory).each do |name|
|
|
170
|
+
path = File.join(@working_directory, name)
|
|
171
|
+
if File.ftype(path) == "file"
|
|
172
|
+
uri = make_resource_uri(name, @domain)
|
|
173
|
+
Resource[uri].link_from(path)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Writes output tuples into the tuple space server.
|
|
179
|
+
def write_output_tuples
|
|
180
|
+
@outputs.flatten.compact.each {|output| write(output)}
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
module Pione
|
|
2
|
+
# RuleHandler is a handler for rule execution.
|
|
3
|
+
module RuleHandler
|
|
4
|
+
# Exception class for rule execution failure.
|
|
5
|
+
class RuleExecutionError < StandardError
|
|
6
|
+
def initialize(handler)
|
|
7
|
+
@handler = handler
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def message
|
|
11
|
+
"Execution error when handling the rule '%s': inputs=%s, output=%s, params=%s" % [
|
|
12
|
+
@handler.rule.rule_path,
|
|
13
|
+
@handler.inputs,
|
|
14
|
+
@handler.outputs,
|
|
15
|
+
@handler.params.inspect
|
|
16
|
+
]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class UnknownRule < StandardError; end
|
|
21
|
+
|
|
22
|
+
# BasicHandler is a base class for rule handlers.
|
|
23
|
+
class BasicHandler
|
|
24
|
+
include TupleSpaceServerInterface
|
|
25
|
+
|
|
26
|
+
attr_reader :rule
|
|
27
|
+
attr_reader :inputs
|
|
28
|
+
attr_reader :outputs
|
|
29
|
+
attr_reader :params
|
|
30
|
+
attr_reader :base_uri
|
|
31
|
+
attr_reader :task_id
|
|
32
|
+
attr_reader :domain
|
|
33
|
+
attr_reader :variable_table
|
|
34
|
+
attr_reader :call_stack
|
|
35
|
+
|
|
36
|
+
# Create a new handler for rule.
|
|
37
|
+
# @param [TupleSpaceServer] ts_server
|
|
38
|
+
# tuple space server
|
|
39
|
+
# @param [Rule] rule
|
|
40
|
+
# rule instance
|
|
41
|
+
# @param [Array<Data,Array<Data>>] inputs
|
|
42
|
+
# input tuples
|
|
43
|
+
# @param [Hash] opts
|
|
44
|
+
# optionals
|
|
45
|
+
def initialize(ts_server, rule, inputs, params, call_stack, opts={})
|
|
46
|
+
# check arguments
|
|
47
|
+
raise ArgumentError.new(inputs) unless inputs.kind_of?(Array)
|
|
48
|
+
raise ArgumentError.new(inputs) unless inputs.size == rule.inputs.size
|
|
49
|
+
raise ArgumentError.new(params) unless params.kind_of?(Parameters)
|
|
50
|
+
|
|
51
|
+
# set tuple space server
|
|
52
|
+
set_tuple_space_server(ts_server)
|
|
53
|
+
|
|
54
|
+
# set informations
|
|
55
|
+
@rule = rule
|
|
56
|
+
@inputs = inputs
|
|
57
|
+
@outputs = []
|
|
58
|
+
@params = @rule.params.merge(params)
|
|
59
|
+
@original_params = params
|
|
60
|
+
@content = rule.body
|
|
61
|
+
@domain = get_handling_domain(opts)
|
|
62
|
+
@variable_table = VariableTable.new(@params.data)
|
|
63
|
+
@base_uri = read(Tuple[:base_uri].any).uri
|
|
64
|
+
@dry_run = begin read0(Tuple[:dry_run].any).availability rescue false end
|
|
65
|
+
@task_id = ID.task_id(@inputs, @params)
|
|
66
|
+
@call_stack = call_stack
|
|
67
|
+
|
|
68
|
+
setup_variable_table
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Puts environment variable into pione variable table.
|
|
72
|
+
# @param [Hash{String => String}] env
|
|
73
|
+
# environment table
|
|
74
|
+
# @return [void]
|
|
75
|
+
def setenv(env)
|
|
76
|
+
env.each do |key, value|
|
|
77
|
+
@variable_table.set(Variable.new("ENV_" + key), PioneString.new(value))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Handles the rule and returns the outputs.
|
|
82
|
+
# @return [Array<Data,Array<Data>>]
|
|
83
|
+
# outputs
|
|
84
|
+
def handle
|
|
85
|
+
name = self.class.message_name
|
|
86
|
+
|
|
87
|
+
# show begin message
|
|
88
|
+
user_message_begin("Start %s Rule: %s" % [name, handler_digest])
|
|
89
|
+
|
|
90
|
+
# call stack
|
|
91
|
+
debug_message("call stack:")
|
|
92
|
+
@call_stack.each_with_index do |domain, i|
|
|
93
|
+
debug_message("%s:%s" % [i, domain], 1)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# execute the rule
|
|
97
|
+
outputs = execute
|
|
98
|
+
|
|
99
|
+
# show output list
|
|
100
|
+
debug_message("%s Rule %s Result:" % [name, handler_digest])
|
|
101
|
+
|
|
102
|
+
@outputs.compact.each_with_index do |output, i|
|
|
103
|
+
if output.kind_of?(Array)
|
|
104
|
+
output.each_with_index do |o, ii|
|
|
105
|
+
debug_message("%s,%s:%s" % [i, ii, o.name], 1)
|
|
106
|
+
end
|
|
107
|
+
else
|
|
108
|
+
debug_message("%s:%s" % [i, output.name], 1)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# show end message
|
|
113
|
+
user_message_end "End %s Rule: %s" % [name, handler_digest]
|
|
114
|
+
|
|
115
|
+
return outputs.compact
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Returns true if it is root rule handler.
|
|
119
|
+
# @return [Boolean]
|
|
120
|
+
# true if it is root rule handler
|
|
121
|
+
def root?
|
|
122
|
+
self.kind_of?(RootHandler)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @api private
|
|
126
|
+
def ==(other)
|
|
127
|
+
return false unless @rule == other.rule
|
|
128
|
+
return false unless @inputs == other.inputs
|
|
129
|
+
return false unless @outputs == other.outputs
|
|
130
|
+
return false unless @params == other.params
|
|
131
|
+
return true
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @api private
|
|
135
|
+
alias :eql? :==
|
|
136
|
+
|
|
137
|
+
# @api private
|
|
138
|
+
def hash
|
|
139
|
+
@rule.hash + @inputs.hash + @outputs.hash + @params.hash
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
# Executes the rule.
|
|
145
|
+
# @return [Array<Data,Array<Data>>]
|
|
146
|
+
# outputs
|
|
147
|
+
# @api private
|
|
148
|
+
def execute
|
|
149
|
+
raise NotImplementError
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Return the domain.
|
|
153
|
+
def get_handling_domain(opts)
|
|
154
|
+
opts[:domain] || ID.domain_id(
|
|
155
|
+
@rule.expr.package.name,
|
|
156
|
+
@rule.expr.name,
|
|
157
|
+
@inputs,
|
|
158
|
+
@original_params
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Makes resource uri.
|
|
163
|
+
def make_resource_uri(name, domain)
|
|
164
|
+
if domain == "root" || domain.nil?
|
|
165
|
+
return URI.parse(@base_uri) + "./%s" % name
|
|
166
|
+
else
|
|
167
|
+
# make relative path
|
|
168
|
+
rule_name = domain.split("_")[0..-2].join("_")
|
|
169
|
+
digest = domain.split("_").last
|
|
170
|
+
path = "./.%s/%s/%s" % [rule_name, digest, name]
|
|
171
|
+
|
|
172
|
+
# make uri
|
|
173
|
+
return URI.parse(@base_uri) + path
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Makes output resource uri.
|
|
178
|
+
def make_output_resource_uri(name)
|
|
179
|
+
# get parent domain or root domain
|
|
180
|
+
make_resource_uri(name, @call_stack.last)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Make output tuple by name.
|
|
184
|
+
def make_output_tuple(name)
|
|
185
|
+
uri = make_output_resource_uri(name).to_s
|
|
186
|
+
Tuple[:data].new(name: name, domain: @domain, uri: uri, time: nil)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Setup variable table. The following variables are introduced in variable
|
|
190
|
+
# table:
|
|
191
|
+
# - input auto variables
|
|
192
|
+
# - output auto variables
|
|
193
|
+
def setup_variable_table
|
|
194
|
+
@variable_table.make_input_auto_variables(@rule.inputs, @inputs)
|
|
195
|
+
outputs = @rule.outputs.map {|expr| expr.eval(@variable_table) }
|
|
196
|
+
output_tuples = outputs.map {|expr| make_output_tuple(expr.name) }
|
|
197
|
+
@variable_table.make_output_auto_variables(outputs, output_tuples)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Returns digest string of this handler.
|
|
201
|
+
def handler_digest
|
|
202
|
+
params = @params.data.select{|k,_|
|
|
203
|
+
not(k.toplevel?)
|
|
204
|
+
}.map{|k,v| "%s:%s" % [k.name, v.textize]}.join(",")
|
|
205
|
+
"%s([%s],{%s})" % [
|
|
206
|
+
@rule.rule_path,
|
|
207
|
+
@inputs.map{|i|
|
|
208
|
+
i.kind_of?(Array) ? "[%s, ...]" % i[0].name : i.name
|
|
209
|
+
}.join(","),
|
|
210
|
+
params
|
|
211
|
+
]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|