stn-dcell 0.16.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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +7 -0
- data/.rspec +4 -0
- data/.travis.yml +30 -0
- data/CHANGES.md +53 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +20 -0
- data/README.md +168 -0
- data/Rakefile +4 -0
- data/benchmarks/messaging.rb +73 -0
- data/benchmarks/receiver.rb +37 -0
- data/dcell.gemspec +29 -0
- data/examples/itchy.rb +26 -0
- data/examples/scratchy.rb +12 -0
- data/explorer/css/bootstrap-responsive.css +686 -0
- data/explorer/css/bootstrap-responsive.min.css +12 -0
- data/explorer/css/bootstrap.css +3990 -0
- data/explorer/css/bootstrap.min.css +689 -0
- data/explorer/css/explorer.css +28 -0
- data/explorer/ico/favicon.ico +0 -0
- data/explorer/img/glyphicons-halflings-white.png +0 -0
- data/explorer/img/glyphicons-halflings.png +0 -0
- data/explorer/img/logo.png +0 -0
- data/explorer/index.html.erb +94 -0
- data/explorer/js/bootstrap.js +1726 -0
- data/explorer/js/bootstrap.min.js +6 -0
- data/lib/dcell.rb +127 -0
- data/lib/dcell/actor_proxy.rb +30 -0
- data/lib/dcell/celluloid_ext.rb +120 -0
- data/lib/dcell/directory.rb +31 -0
- data/lib/dcell/explorer.rb +74 -0
- data/lib/dcell/future_proxy.rb +32 -0
- data/lib/dcell/global.rb +23 -0
- data/lib/dcell/info_service.rb +122 -0
- data/lib/dcell/mailbox_proxy.rb +53 -0
- data/lib/dcell/messages.rb +65 -0
- data/lib/dcell/node.rb +138 -0
- data/lib/dcell/node_manager.rb +79 -0
- data/lib/dcell/registries/cassandra_adapter.rb +126 -0
- data/lib/dcell/registries/mongodb_adapter.rb +85 -0
- data/lib/dcell/registries/redis_adapter.rb +95 -0
- data/lib/dcell/registries/zk_adapter.rb +123 -0
- data/lib/dcell/responses.rb +16 -0
- data/lib/dcell/router.rb +46 -0
- data/lib/dcell/rpc.rb +95 -0
- data/lib/dcell/rspec.rb +1 -0
- data/lib/dcell/server.rb +73 -0
- data/lib/dcell/version.rb +3 -0
- data/logo.png +0 -0
- data/spec/dcell/actor_proxy_spec.rb +72 -0
- data/spec/dcell/celluloid_ext_spec.rb +32 -0
- data/spec/dcell/directory_spec.rb +22 -0
- data/spec/dcell/explorer_spec.rb +17 -0
- data/spec/dcell/global_spec.rb +25 -0
- data/spec/dcell/node_spec.rb +23 -0
- data/spec/dcell/registries/mongodb_adapter_spec.rb +7 -0
- data/spec/dcell/registries/redis_adapter_spec.rb +6 -0
- data/spec/dcell/registries/zk_adapter_spec.rb +28 -0
- data/spec/options/01-options.rb +10 -0
- data/spec/options/02-registry.rb +13 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/helpers.rb +46 -0
- data/spec/support/registry_examples.rb +35 -0
- data/spec/test_node.rb +38 -0
- data/tasks/cassandra.task +84 -0
- data/tasks/rspec.task +7 -0
- data/tasks/zookeeper.task +58 -0
- metadata +239 -0
@@ -0,0 +1,6 @@
|
|
1
|
+
/**
|
2
|
+
* Bootstrap.js by @fat & @mdo
|
3
|
+
* Copyright 2012 Twitter, Inc.
|
4
|
+
* http://www.apache.org/licenses/LICENSE-2.0.txt
|
5
|
+
*/
|
6
|
+
!function(a){a(function(){"use strict",a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger("close").removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;return!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}"use strict";var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}"use strict";var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this;if(this.isShown)return;a("body").addClass("modal-open"),this.isShown=!0,this.$element.trigger("show"),g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");!b.$element.parent().length&&b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();if(!this.isShown)return;var e=this;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.trigger("hide").removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.show?c.show():(c.hoverState="in",setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show))},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.hide?c.hide():(c.hoverState="out",setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide))},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},setContent:function(){var a=this.tip();a.find(".tooltip-inner").html(this.getTitle()),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a=(a||"").toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,delay:0,selector:!1,placement:"top",trigger:"hover",title:"",template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'}}(window.jQuery),!function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var b=this.tip(),c=this.getTitle(),d=this.getContent();b.find(".popover-title")[a.type(c)=="object"?"append":"html"](c),b.find(".popover-content > *")[a.type(d)=="object"?"append":"html"](d),b.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a=a.toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body").on("click.scroll.data-api",this.selector,d),this.refresh(),this.process()}"use strict",b.prototype={constructor:b,refresh:function(){this.targets=this.$body.find(this.selector).map(function(){var b=a(this).attr("href");return/^#\w/.test(b)&&a(b).length?b:null}),this.offsets=a.map(this.targets,function(b){return a(b).position().top})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.offsets,c=this.targets,d=this.activeTarget,e;for(e=b.length;e--;)d!=c[e]&&a>=b[e]&&(!b[e+1]||a<=b[e+1])&&this.activate(c[e])},activate:function(a){var b;this.activeTarget=a,this.$body.find(this.selector).parent(".active").removeClass("active"),b=this.$body.find(this.selector+'[href="'+a+'"]').parent("li").addClass("active"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],b.trigger({type:"show",relatedTarget:e}),f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(a),this.$element.change(),this.hide()},show:function(){var b=a.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){if(c.matcher(a))return a}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){return a.replace(new RegExp("("+this.query+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},keypress:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery);
|
data/lib/dcell.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
require 'reel'
|
3
|
+
require 'celluloid/zmq'
|
4
|
+
require 'socket'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
Celluloid::ZMQ.init
|
8
|
+
|
9
|
+
require 'dcell/version'
|
10
|
+
require 'dcell/actor_proxy'
|
11
|
+
require 'dcell/directory'
|
12
|
+
require 'dcell/mailbox_proxy'
|
13
|
+
require 'dcell/messages'
|
14
|
+
require 'dcell/node'
|
15
|
+
require 'dcell/node_manager'
|
16
|
+
require 'dcell/global'
|
17
|
+
require 'dcell/responses'
|
18
|
+
require 'dcell/router'
|
19
|
+
require 'dcell/rpc'
|
20
|
+
require 'dcell/future_proxy'
|
21
|
+
require 'dcell/server'
|
22
|
+
require 'dcell/info_service'
|
23
|
+
|
24
|
+
require 'dcell/registries/redis_adapter'
|
25
|
+
|
26
|
+
require 'dcell/celluloid_ext'
|
27
|
+
|
28
|
+
# Distributed Celluloid
|
29
|
+
module DCell
|
30
|
+
class NotConfiguredError < RuntimeError; end # Not configured yet
|
31
|
+
|
32
|
+
@config_lock = Mutex.new
|
33
|
+
|
34
|
+
class << self
|
35
|
+
attr_reader :me, :registry
|
36
|
+
|
37
|
+
# Configure DCell with the following options:
|
38
|
+
#
|
39
|
+
# * id: to identify the local node, defaults to hostname
|
40
|
+
# * addr: 0MQ address of the local node (e.g. tcp://4.3.2.1:7777)
|
41
|
+
# *
|
42
|
+
def setup(options = {})
|
43
|
+
# Stringify keys :/
|
44
|
+
options = options.inject({}) { |h,(k,v)| h[k.to_s] = v; h }
|
45
|
+
|
46
|
+
@config_lock.synchronize do
|
47
|
+
@configuration = {
|
48
|
+
'id' => generate_node_id,
|
49
|
+
'addr' => "tcp://127.0.0.1:*",
|
50
|
+
'registry' => {'adapter' => 'redis', 'server' => 'localhost'}
|
51
|
+
}.merge(options)
|
52
|
+
|
53
|
+
@me = Node.new @configuration['id'], nil
|
54
|
+
|
55
|
+
registry_adapter = @configuration['registry'][:adapter] || @configuration['registry']['adapter']
|
56
|
+
raise ArgumentError, "no registry adapter given in config" unless registry_adapter
|
57
|
+
|
58
|
+
registry_class_name = registry_adapter.split("_").map(&:capitalize).join << "Adapter"
|
59
|
+
|
60
|
+
begin
|
61
|
+
registry_class = DCell::Registry.const_get registry_class_name
|
62
|
+
rescue NameError
|
63
|
+
raise ArgumentError, "invalid registry adapter: #{@configuration['registry']['adapter']}"
|
64
|
+
end
|
65
|
+
|
66
|
+
@registry = registry_class.new(@configuration['registry'])
|
67
|
+
|
68
|
+
ObjectSpace.define_finalizer(me, proc {Directory.remove @configuration['id']})
|
69
|
+
end
|
70
|
+
|
71
|
+
me
|
72
|
+
end
|
73
|
+
|
74
|
+
# Obtain the local node ID
|
75
|
+
def id
|
76
|
+
raise NotConfiguredError, "please configure DCell with DCell.setup" unless @configuration
|
77
|
+
@configuration['id']
|
78
|
+
end
|
79
|
+
|
80
|
+
# Obtain the 0MQ address to the local mailbox
|
81
|
+
def addr; @configuration['addr']; end
|
82
|
+
alias_method :address, :addr
|
83
|
+
|
84
|
+
def addr=(addr)
|
85
|
+
@configuration['addr'] = addr
|
86
|
+
@me.update_server_address addr
|
87
|
+
end
|
88
|
+
alias_method :address=, :addr=
|
89
|
+
|
90
|
+
# Attempt to generate a unique node ID for this machine
|
91
|
+
def generate_node_id
|
92
|
+
# a little bit more creative
|
93
|
+
if @registry.respond_to? :unique
|
94
|
+
@registry.unique
|
95
|
+
else
|
96
|
+
digest = Digest::SHA512.new
|
97
|
+
seed = Socket.gethostname + rand.to_s + Time.now.to_s + SecureRandom.hex
|
98
|
+
digest.update(seed).to_s
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Run the DCell application
|
103
|
+
def run
|
104
|
+
DCell::SupervisionGroup.run
|
105
|
+
end
|
106
|
+
|
107
|
+
# Run the DCell application in the background
|
108
|
+
def run!
|
109
|
+
DCell::SupervisionGroup.run!
|
110
|
+
end
|
111
|
+
|
112
|
+
# Start combines setup and run! into a single step
|
113
|
+
def start(options = {})
|
114
|
+
setup options
|
115
|
+
run!
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# DCell's actor dependencies
|
120
|
+
class SupervisionGroup < Celluloid::SupervisionGroup
|
121
|
+
supervise NodeManager, :as => :node_manager
|
122
|
+
supervise Server, :as => :dcell_server
|
123
|
+
supervise InfoService, :as => :info
|
124
|
+
end
|
125
|
+
|
126
|
+
Logger = Celluloid::Logger
|
127
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module DCell
|
2
|
+
# Proxy object for actors that live on remote nodes
|
3
|
+
class CellProxy < Celluloid::CellProxy; end
|
4
|
+
|
5
|
+
class ThreadHandleProxy
|
6
|
+
def kill
|
7
|
+
raise NotImplementedError, "remote kill not supported"
|
8
|
+
end
|
9
|
+
|
10
|
+
def join
|
11
|
+
raise NotImplementedError, "remote join not supported"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class SubjectProxy
|
16
|
+
def class
|
17
|
+
"[remote]"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Actor
|
22
|
+
def initialize(mailbox)
|
23
|
+
@mailbox = mailbox
|
24
|
+
@thread = ThreadHandleProxy.new
|
25
|
+
@subject = SubjectProxy.new
|
26
|
+
@proxy = Celluloid::ActorProxy.new(@thread, @mailbox)
|
27
|
+
end
|
28
|
+
attr_reader :mailbox, :thread, :subject, :proxy
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# Celluloid mailboxes are the universal message exchange points. You won't
|
2
|
+
# be able to marshal them though, unfortunately, because they contain
|
3
|
+
# mutexes.
|
4
|
+
#
|
5
|
+
# DCell provides a message routing layer between nodes that can direct
|
6
|
+
# messages back to local mailboxes. To accomplish this, DCell adds custom
|
7
|
+
# marshalling to mailboxes so that if they're unserialized on a remote
|
8
|
+
# node you instead get a proxy object that routes messages through the
|
9
|
+
# DCell overlay network back to the node where the actor actually exists
|
10
|
+
|
11
|
+
module Celluloid
|
12
|
+
class CellProxy
|
13
|
+
# Marshal uses respond_to? to determine if this object supports _dump so
|
14
|
+
# unfortunately we have to monkeypatch in _dump support as the proxy
|
15
|
+
# itself normally jacks respond_to? and proxies to the actor
|
16
|
+
alias_method :__respond_to?, :respond_to?
|
17
|
+
def respond_to?(meth, check_private = false)
|
18
|
+
return false if meth == :marshal_dump
|
19
|
+
return true if meth == :_dump
|
20
|
+
__respond_to?(meth, check_private)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Dump an actor proxy via its mailbox
|
24
|
+
def _dump(level)
|
25
|
+
@mailbox._dump(level)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create an actor proxy object which routes messages over DCell's overlay
|
29
|
+
# network and back to the original mailbox
|
30
|
+
def self._load(string)
|
31
|
+
mailbox = ::Celluloid::Mailbox._load(string)
|
32
|
+
|
33
|
+
case mailbox
|
34
|
+
when ::DCell::MailboxProxy
|
35
|
+
actor = ::DCell::Actor.new(mailbox)
|
36
|
+
::DCell::CellProxy.new actor.proxy, mailbox, actor.subject.class.to_s
|
37
|
+
when ::Celluloid::Mailbox
|
38
|
+
actor = find_actor(mailbox)
|
39
|
+
::Celluloid::CellProxy.new actor.proxy, mailbox, actor.behavior.subject.class.to_s
|
40
|
+
else
|
41
|
+
::Kernel.raise "funny, I did not expect to see a #{mailbox.class} here"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.find_actor(mailbox)
|
46
|
+
::Thread.list.each do |t|
|
47
|
+
if actor = t[:celluloid_actor]
|
48
|
+
return actor if actor.mailbox == mailbox
|
49
|
+
end
|
50
|
+
end
|
51
|
+
::Kernel.raise "no actor found for mailbox: #{mailbox.inspect}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Mailbox
|
56
|
+
def address
|
57
|
+
"#{@address}@#{DCell.id}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# This custom dumper registers actors with the DCell registry so they can
|
61
|
+
# be reached remotely.
|
62
|
+
def _dump(level)
|
63
|
+
DCell::Router.register self
|
64
|
+
address
|
65
|
+
end
|
66
|
+
|
67
|
+
# Create a mailbox proxy object which routes messages over DCell's overlay
|
68
|
+
# network and back to the original mailbox
|
69
|
+
def self._load(string)
|
70
|
+
DCell::MailboxProxy._load(string)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class SyncCall
|
75
|
+
def _dump(level)
|
76
|
+
uuid = DCell::RPC::Manager.register self
|
77
|
+
payload = Marshal.dump([@sender,@method,@arguments,@block])
|
78
|
+
"#{uuid}@#{DCell.id}:rpc:#{payload}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def self._load(string)
|
82
|
+
DCell::RPC._load(string)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class BlockProxy
|
87
|
+
def _dump(level)
|
88
|
+
uuid = DCell::RPC::Manager.register self
|
89
|
+
payload = Marshal.dump([@mailbox,@execution,@arguments])
|
90
|
+
"#{uuid}@#{DCell.id}:rpb:#{payload}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def self._load(string)
|
94
|
+
DCell::RPC._load(string)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class BlockCall
|
99
|
+
def _dump(level)
|
100
|
+
uuid = DCell::RPC::Manager.register self
|
101
|
+
payload = Marshal.dump([@block_proxy,@sender,@arguments])
|
102
|
+
"#{uuid}@#{DCell.id}:rpbc:#{payload}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def self._load(string)
|
106
|
+
DCell::RPC._load(string)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Future
|
111
|
+
def _dump(level)
|
112
|
+
mailbox_id = DCell::Router.register self
|
113
|
+
"#{mailbox_id}@#{DCell.id}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def self._load(string)
|
117
|
+
DCell::FutureProxy._load(string)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DCell
|
2
|
+
# Directory of nodes connected to the DCell cluster
|
3
|
+
module Directory
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Get the URL for a particular Node ID
|
7
|
+
def get(node_id)
|
8
|
+
DCell.registry.get_node node_id
|
9
|
+
end
|
10
|
+
alias_method :[], :get
|
11
|
+
|
12
|
+
# Set the address of a particular Node ID
|
13
|
+
def set(node_id, addr)
|
14
|
+
DCell.registry.set_node node_id, addr
|
15
|
+
end
|
16
|
+
alias_method :[]=, :set
|
17
|
+
|
18
|
+
# List all of the node IDs in the directory
|
19
|
+
def all
|
20
|
+
DCell.registry.nodes
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear_all
|
24
|
+
DCell.registry.clear_all_nodes
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove(node)
|
28
|
+
DCell.registry.remove_node node
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'dcell'
|
2
|
+
require 'reel'
|
3
|
+
require 'pathname'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module DCell
|
7
|
+
# Web UI for DCell
|
8
|
+
# TODO: rewrite this entire thing with less hax
|
9
|
+
class Explorer < Reel::Server
|
10
|
+
ASSET_ROOT = Pathname.new File.expand_path("../../../explorer", __FILE__)
|
11
|
+
|
12
|
+
def initialize(host = "127.0.0.1", port = 7778)
|
13
|
+
super(host, port, &method(:on_connection))
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_connection(connection)
|
17
|
+
request = connection.request
|
18
|
+
return unless request
|
19
|
+
route connection, request
|
20
|
+
end
|
21
|
+
|
22
|
+
def route(connection, request)
|
23
|
+
if request.url == "/"
|
24
|
+
path = "index.html"
|
25
|
+
else
|
26
|
+
path = request.url[%r{^/([a-z0-9\.\-_]+(/[a-z0-9\.\-_]+)*)$}, 1]
|
27
|
+
end
|
28
|
+
|
29
|
+
if !path or path[".."]
|
30
|
+
Logger.info "404 Not Found: #{request.path}"
|
31
|
+
connection.respond :not_found, "Not found"
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
render_resource connection, path
|
36
|
+
end
|
37
|
+
|
38
|
+
def render_resource(connection, path)
|
39
|
+
if node_id = path[%r{^nodes/(.*)$}, 1]
|
40
|
+
node = DCell::Node[node_id]
|
41
|
+
path = "index.html"
|
42
|
+
else
|
43
|
+
node = DCell.me
|
44
|
+
end
|
45
|
+
|
46
|
+
asset_path = ASSET_ROOT.join(path)
|
47
|
+
if asset_path.exist?
|
48
|
+
asset_path.open("r") do |file|
|
49
|
+
connection.respond :ok, file
|
50
|
+
end
|
51
|
+
|
52
|
+
Logger.info "200 OK: /#{path}"
|
53
|
+
elsif File.exist?(asset_path.to_s + ".erb") and node
|
54
|
+
connection.respond :ok, render_template(asset_path.to_s + ".erb", node)
|
55
|
+
Logger.info "200 OK: /#{path}"
|
56
|
+
else
|
57
|
+
connection.respond :not_found, "Not found"
|
58
|
+
Logger.info "404 Not Found: /#{path}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def render_template(template, node)
|
63
|
+
@node = node
|
64
|
+
@info = @node[:info].to_hash
|
65
|
+
|
66
|
+
template = ERB.new File.read(template, :mode => 'rb')
|
67
|
+
template.result(binding)
|
68
|
+
end
|
69
|
+
|
70
|
+
def node_path(node)
|
71
|
+
"/nodes/#{node.id}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DCell
|
2
|
+
class FutureProxy
|
3
|
+
def initialize(mailbox_id,node_id,node_addr)
|
4
|
+
@mailbox_id = mailbox_id
|
5
|
+
@node_id = node_id
|
6
|
+
@node_addr = node_addr
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(message)
|
10
|
+
node = Node[@node_id]
|
11
|
+
node = Node.new(@node_id, @node_addr) unless node
|
12
|
+
node.async.send_message Message::Relay.new(self, message)
|
13
|
+
end
|
14
|
+
|
15
|
+
def _dump(level)
|
16
|
+
"#{@mailbox_id}@#{@node_id}@#{@node_addr}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Loader for custom marshal format
|
20
|
+
def self._load(string)
|
21
|
+
mailbox_id, node_id, node_addr = string.split("@")
|
22
|
+
|
23
|
+
if node_id == DCell.id
|
24
|
+
future = Router.find(mailbox_id)
|
25
|
+
raise "tried to unmarshal dead Celluloid::Future: #{mailbox_id}" unless future
|
26
|
+
future
|
27
|
+
else
|
28
|
+
new(mailbox_id, node_id, node_addr)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/dcell/global.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module DCell
|
2
|
+
# Global object registry shared among all DCell nodes
|
3
|
+
module Global
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Get a global value
|
7
|
+
def get(key)
|
8
|
+
DCell.registry.get_global key.to_s
|
9
|
+
end
|
10
|
+
alias_method :[], :get
|
11
|
+
|
12
|
+
# Set a global value
|
13
|
+
def set(key, value)
|
14
|
+
DCell.registry.set_global key.to_s, value
|
15
|
+
end
|
16
|
+
alias_method :[]=, :set
|
17
|
+
|
18
|
+
# Get the keys for all the globals in the system
|
19
|
+
def keys
|
20
|
+
DCell.registry.global_keys.map(&:to_sym)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|