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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +7 -0
  4. data/.rspec +4 -0
  5. data/.travis.yml +30 -0
  6. data/CHANGES.md +53 -0
  7. data/Gemfile +18 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +168 -0
  10. data/Rakefile +4 -0
  11. data/benchmarks/messaging.rb +73 -0
  12. data/benchmarks/receiver.rb +37 -0
  13. data/dcell.gemspec +29 -0
  14. data/examples/itchy.rb +26 -0
  15. data/examples/scratchy.rb +12 -0
  16. data/explorer/css/bootstrap-responsive.css +686 -0
  17. data/explorer/css/bootstrap-responsive.min.css +12 -0
  18. data/explorer/css/bootstrap.css +3990 -0
  19. data/explorer/css/bootstrap.min.css +689 -0
  20. data/explorer/css/explorer.css +28 -0
  21. data/explorer/ico/favicon.ico +0 -0
  22. data/explorer/img/glyphicons-halflings-white.png +0 -0
  23. data/explorer/img/glyphicons-halflings.png +0 -0
  24. data/explorer/img/logo.png +0 -0
  25. data/explorer/index.html.erb +94 -0
  26. data/explorer/js/bootstrap.js +1726 -0
  27. data/explorer/js/bootstrap.min.js +6 -0
  28. data/lib/dcell.rb +127 -0
  29. data/lib/dcell/actor_proxy.rb +30 -0
  30. data/lib/dcell/celluloid_ext.rb +120 -0
  31. data/lib/dcell/directory.rb +31 -0
  32. data/lib/dcell/explorer.rb +74 -0
  33. data/lib/dcell/future_proxy.rb +32 -0
  34. data/lib/dcell/global.rb +23 -0
  35. data/lib/dcell/info_service.rb +122 -0
  36. data/lib/dcell/mailbox_proxy.rb +53 -0
  37. data/lib/dcell/messages.rb +65 -0
  38. data/lib/dcell/node.rb +138 -0
  39. data/lib/dcell/node_manager.rb +79 -0
  40. data/lib/dcell/registries/cassandra_adapter.rb +126 -0
  41. data/lib/dcell/registries/mongodb_adapter.rb +85 -0
  42. data/lib/dcell/registries/redis_adapter.rb +95 -0
  43. data/lib/dcell/registries/zk_adapter.rb +123 -0
  44. data/lib/dcell/responses.rb +16 -0
  45. data/lib/dcell/router.rb +46 -0
  46. data/lib/dcell/rpc.rb +95 -0
  47. data/lib/dcell/rspec.rb +1 -0
  48. data/lib/dcell/server.rb +73 -0
  49. data/lib/dcell/version.rb +3 -0
  50. data/logo.png +0 -0
  51. data/spec/dcell/actor_proxy_spec.rb +72 -0
  52. data/spec/dcell/celluloid_ext_spec.rb +32 -0
  53. data/spec/dcell/directory_spec.rb +22 -0
  54. data/spec/dcell/explorer_spec.rb +17 -0
  55. data/spec/dcell/global_spec.rb +25 -0
  56. data/spec/dcell/node_spec.rb +23 -0
  57. data/spec/dcell/registries/mongodb_adapter_spec.rb +7 -0
  58. data/spec/dcell/registries/redis_adapter_spec.rb +6 -0
  59. data/spec/dcell/registries/zk_adapter_spec.rb +28 -0
  60. data/spec/options/01-options.rb +10 -0
  61. data/spec/options/02-registry.rb +13 -0
  62. data/spec/spec_helper.rb +28 -0
  63. data/spec/support/helpers.rb +46 -0
  64. data/spec/support/registry_examples.rb +35 -0
  65. data/spec/test_node.rb +38 -0
  66. data/tasks/cassandra.task +84 -0
  67. data/tasks/rspec.task +7 -0
  68. data/tasks/zookeeper.task +58 -0
  69. 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
@@ -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