remove trailing whitespaces ❤️

This commit is contained in:
Gosha Arinich
2013-02-26 07:31:35 +03:00
parent b50e0536c7
commit cafc75b238
383 changed files with 4220 additions and 2221 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:test) do |spec|
spec.pattern = 'spec/*_spec.rb'
+1 -1
View File
@@ -7,7 +7,7 @@ module DiscourseEmoji
initializer "discourse_emoji.configure_rails_initialization" do |app|
app.config.after_initialize do
app.config.after_initialize do
DiscoursePluginRegistry.setup(DiscourseEmoji::Plugin)
Post.white_listed_image_classes << "emoji"
end
+2 -2
View File
@@ -3,10 +3,10 @@ require 'discourse_plugin'
module DiscourseEmoji
class Plugin < DiscoursePlugin
def setup
# Add our Assets
register_js('discourse_emoji',
register_js('discourse_emoji',
server_side: File.expand_path('../../../vendor/assets/javascripts/discourse_emoji.js', __FILE__))
register_css('discourse_emoji')
end
+4 -4
View File
@@ -9,15 +9,15 @@ describe DiscourseEmoji::Plugin do
context '.setup' do
it 'registers its js' do
it 'registers its js' do
plugin.expects(:register_js).with('discourse_emoji', any_parameters)
plugin.setup
end
end
it 'registers its css' do
it 'registers its css' do
plugin.expects(:register_css).with('discourse_emoji')
plugin.setup
end
end
end
+1 -1
View File
@@ -7,7 +7,7 @@ RSpec.configure do |config|
config.mock_framework = :mocha
config.color_enabled = true
end
@@ -10,9 +10,9 @@
style = ""
if (opts && opts.environment === "email") {
// Hard code sizes for email view
style = 'width="20" height="20"';
style = 'width="20" height="20"';
}
this.textResult = text.replace(/\:([a-z\_\+\-0-9]+)\:/g, function (m1, m2) {
return (emoji.indexOf(m2) !== -1) ?
'<img alt="' + m2 + '" title=":' + m2 + ':" src="/assets/emoji/' + m2 + '.png" ' + style + ' class="emoji"/>' :
@@ -23,7 +23,7 @@
if (Discourse && Discourse.ComposerView) {
Discourse.ComposerView.on("initWmdEditor", function(event){
template = Handlebars.compile("<div class='autocomplete'>" +
"<ul>" +
"{{#each options}}" +
@@ -38,7 +38,7 @@
$('#wmd-input').autocomplete({
template: template,
key: ":",
key: ":",
transformComplete: function(v){ return v + ":"; },
dataSource: function(term, callback){
@@ -50,14 +50,14 @@
}
var options = []
var i;
var i;
for (i=0; i < emoji.length; i++) {
if (emoji[i].indexOf(term) == 0) {
options.push(emoji[i]);
if(options.length > 4) { break; }
}
}
if (options.length <= 4) {
for (i=0; i < emoji.length; i++) {
if (emoji[i].indexOf(term) > 0) {
@@ -66,7 +66,7 @@
}
}
}
callback(options)
}
});
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:test) do |spec|
spec.pattern = 'spec/*_spec.rb'
+1 -1
View File
@@ -8,7 +8,7 @@ RSpec.configure do |config|
config.mock_framework = :mocha
config.color_enabled = true
config.before(:each) do
DiscourseEvent.clear
end
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:test) do |spec|
spec.pattern = 'spec/*_spec.rb'
+2 -2
View File
@@ -7,7 +7,7 @@ module DiscoursePoll
initializer "discourse_poll.configure_rails_initialization" do |app|
app.config.after_initialize do
app.config.after_initialize do
DiscoursePluginRegistry.setup(DiscoursePoll::Plugin)
end
@@ -18,4 +18,4 @@ module DiscoursePoll
end
end
end
end
@@ -36,12 +36,12 @@ en:
long_form: 'voted for this post'
archetypes:
poll:
poll:
title: "Poll Topic"
options:
single_vote:
single_vote:
title: "Only allow one vote"
description: "A user may only vote on one post."
private_poll:
private_poll:
title: "Voting is Private"
description: "Hide who voted for what choice."
description: "Hide who voted for what choice."
+5 -5
View File
@@ -5,9 +5,9 @@ module DiscoursePoll
MAX_SORT_ORDER = 2147483647
POLL_OPTIONS = {private_poll: 1, single_vote: 1}
def setup
# Add our Assets
register_js('discourse_poll')
register_css('discourse_poll')
@@ -26,15 +26,15 @@ module DiscoursePoll
post.sort_order = 1
else
post.sort_order = DiscoursePoll::Plugin::MAX_SORT_ORDER
end
end
end
module TopicViewSerializerMixin
def self.included(base)
base.attributes :private_poll, :single_vote
base.attributes :private_poll, :single_vote
end
def private_poll
object.topic.has_meta_data_boolean?(:private_poll)
end
+8 -8
View File
@@ -9,22 +9,22 @@ describe DiscoursePoll::Plugin do
context '.setup' do
it 'registers its js' do
it 'registers its js' do
plugin.expects(:register_js)
plugin.setup
end
end
it 'registers its css' do
it 'registers its css' do
plugin.expects(:register_css)
plugin.setup
end
end
it 'registers a poll archetype' do
it 'registers a poll archetype' do
plugin.expects(:register_archetype).with('poll', DiscoursePoll::Plugin::POLL_OPTIONS)
plugin.setup
end
end
it 'registers a handler on post_create' do
it 'registers a handler on post_create' do
plugin.expects(:listen_for).with(:before_create_post)
plugin.setup
end
@@ -38,7 +38,7 @@ describe DiscoursePoll::Plugin do
it "doesn't set the sort order" do
plugin.before_create_post(post)
post.sort_order.should_not == DiscoursePoll::Plugin::MAX_SORT_ORDER
post.sort_order.should_not == DiscoursePoll::Plugin::MAX_SORT_ORDER
end
end
+1 -1
View File
@@ -7,7 +7,7 @@ RSpec.configure do |config|
config.mock_framework = :mocha
config.color_enabled = true
end
@@ -1,7 +1,7 @@
(function() {
window.Discourse.Post.reopen({
voteAction: function () {
voteAction: function () {
return this.get('actionByName.vote');
}.property('actionByName.vote'),
@@ -12,12 +12,12 @@
}.property('replyBelowUrlComputed', 'topic.archetype'),
// Vote for this post
vote: function() {
vote: function() {
voteType = Discourse.get('site.post_action_types').findProperty('name_key', 'vote');
this.get('voteAction').act();
Em.run.next(function () {
this.set('topic.voted_in_topic', true);
}.bind(this));
}.bind(this));
return false;
},
@@ -37,7 +37,7 @@
this.get('voteAction').undo();
Em.run.next(function () {
this.set('topic.voted_in_topic', false);
}.bind(this));
}.bind(this));
return false;
}
@@ -1,9 +1,9 @@
(function() {
Discourse.PostActionType.reopen({
isVote: function() {
return (this.get('name_key') === 'vote');
}.property('name_key')
});
}).call(this);
}).call(this);
@@ -1,13 +1,13 @@
(function() {
window.Discourse.PostView.reopen({
extraClass: function() {
if (this.get('showVotes')) return 'votes';
return null;
}.property('showVotes'),
showVotes: function() {
var post = this.get('post');
var post = this.get('post');
if (post.get('post_number') === 1) return;
if (post.get('post_type') !== Discourse.Post.REGULAR_TYPE) return;
if (post.get('reply_to_post_number')) return;
@@ -15,4 +15,4 @@
}.property('post.post_number', 'post.post_type', 'post.reply_to_post_number')
})
}).call(this);
}).call(this);
@@ -4,9 +4,9 @@
// Append our template for the poll controls
if (this.get('controller.content.archetype') == 'poll') {
this.get('childViews').pushObject(Discourse.VoteControlsView.create());
this.get('childViews').pushObject(Discourse.VoteControlsView.create());
}
});
}).call(this);
}).call(this);
@@ -6,4 +6,4 @@
}.property()
});
}).call(this);
}).call(this);
@@ -11,4 +11,4 @@
}
});
}).call(this);
}).call(this);
@@ -6,7 +6,7 @@
if (this.get('topic.archetype') === 'poll') {
this.renderIcon(buffer, 'check-empty', 'poll');
}
});
}).call(this);
}).call(this);
@@ -38,4 +38,4 @@
}.property('post.voteAction.can_act')
})
}).call(this);
}).call(this);
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:test) do |spec|
spec.pattern = 'spec/*_spec.rb'
+3 -3
View File
@@ -1,5 +1,5 @@
Rails.application.routes.draw do
Rails.application.routes.draw do
put 't/:slug/:topic_id/complete' => 'topics#complete', :constraints => {:topic_id => /\d+/}
end
end
+2 -2
View File
@@ -7,7 +7,7 @@ module DiscourseTask
initializer "discourse_task.configure_rails_initialization" do |app|
app.config.after_initialize do
app.config.after_initialize do
DiscoursePluginRegistry.setup(DiscourseTask::Plugin)
end
@@ -17,4 +17,4 @@ module DiscourseTask
end
end
end
end
+2 -2
View File
@@ -20,7 +20,7 @@ module DiscourseTask
module TopicViewSerializerMixin
def self.included(base)
base.attributes :can_complete_task, :complete, :completed_at
base.attributes :can_complete_task, :complete, :completed_at
end
def can_complete_task
@@ -73,7 +73,7 @@ module DiscourseTask
end
render nothing: true
end
+6 -6
View File
@@ -8,20 +8,20 @@ describe DiscourseTask::Plugin do
context '.setup' do
it 'registers its js' do
it 'registers its js' do
plugin.expects(:register_js).with('discourse_task')
plugin.setup
end
end
it 'registers its css' do
it 'registers its css' do
plugin.expects(:register_css).with('discourse_task')
plugin.setup
end
end
it 'registers a task archetype' do
it 'registers a task archetype' do
plugin.expects(:register_archetype).with('task')
plugin.setup
end
end
end
+1 -1
View File
@@ -7,7 +7,7 @@ RSpec.configure do |config|
config.mock_framework = :mocha
config.color_enabled = true
end
@@ -1,5 +1,5 @@
(function() {
Discourse.TopicController.reopen({
// Allow the user to complete the task
@@ -10,6 +10,6 @@
})
}).call(this);
}).call(this);
@@ -1,5 +1,5 @@
(function() {
Discourse.Topic.reopen({
// Allow the user to complete the task
@@ -10,14 +10,14 @@
jQuery.ajax(this.get('url') + "/complete", {
type: 'PUT',
data: {
complete: this.get('complete') ? 'true' : 'false'
complete: this.get('complete') ? 'true' : 'false'
}
});
}
})
}).call(this);
}).call(this);
@@ -1,5 +1,5 @@
(function() {
Discourse.TopicFooterButtonsView.prototype.on("additionalButtons", function(childViews) {
var topic = this.get('topic');
if (topic.get('archetype') == 'task' && topic.get('can_complete_task')) {
@@ -15,16 +15,16 @@
renderIcon: function (buffer) {
if (!this.get('complete')) {
buffer.push("<i class='icon-cog'></i>")
}
},
buffer.push("<i class='icon-cog'></i>")
}
},
text: function () {
if (this.get('complete')) {
return Em.String.i18n("task.reverse");
return Em.String.i18n("task.reverse");
} else {
return Em.String.i18n("task.complete_action");
}
}
}.property('complete'),
click: function(e) {
@@ -35,6 +35,6 @@
}
});
}).call(this);
}).call(this);
@@ -9,7 +9,7 @@
if (topic.get('complete')) icon = 'ok';
this.renderIcon(buffer, icon, 'task');
}
});
Discourse.TopicStatusView.reopen({
@@ -18,4 +18,4 @@
}.observes('topic.complete')
})
}).call(this);
}).call(this);
@@ -11,4 +11,4 @@
}
});
}).call(this);
}).call(this);
+2 -2
View File
@@ -44,7 +44,7 @@ App.IndexModel = Ember.Object.extend({
processes = processes.sort(function(a,b){
return a.get('uniqueId') < b.get('uniqueId') ? -1 : 1;
});
// somewhat odd ...
// somewhat odd ...
_this.set('processes', null);
_this.set('processes', processes);
});
@@ -55,7 +55,7 @@ App.IndexModel = Ember.Object.extend({
discover: function(){
var _this = this;
this.set('processes', Em.A());
this.ensureSubscribed();
this.set("discovering", true);
+43 -43
View File
@@ -1752,7 +1752,7 @@ var MapWithDefault = Ember.MapWithDefault = function(options) {
@static
@param [options]
@param {anything} [options.defaultValue]
@return {Ember.MapWithDefault|Ember.Map} If options are passed, returns
@return {Ember.MapWithDefault|Ember.Map} If options are passed, returns
`Ember.MapWithDefault` otherwise returns `Ember.Map`
*/
MapWithDefault.create = function(options) {
@@ -1826,7 +1826,7 @@ var FIRST_KEY = /^([^\.\*]+)/;
If you plan to run on IE8 and older browsers then you should use this
method anytime you want to retrieve a property on an object that you don't
know for sure is private. (Properties beginning with an underscore '_'
know for sure is private. (Properties beginning with an underscore '_'
are considered private.)
On all newer browsers, you only need to use this method to retrieve
@@ -1888,7 +1888,7 @@ get = function get(obj, keyName) {
If you plan to run on IE8 and older browsers then you should use this
method anytime you want to set a property on an object that you don't
know for sure is private. (Properties beginning with an underscore '_'
know for sure is private. (Properties beginning with an underscore '_'
are considered private.)
On all newer browsers, you only need to use this method to set
@@ -4227,7 +4227,7 @@ Ember.RunLoop = RunLoop;
```javascript
Ember.run(function(){
// code to be execute within a RunLoop
// code to be execute within a RunLoop
});
```
@@ -4266,7 +4266,7 @@ var run = Ember.run;
```javascript
Ember.run.begin();
// code to be execute within a RunLoop
// code to be execute within a RunLoop
Ember.run.end();
```
@@ -4284,7 +4284,7 @@ Ember.run.begin = function() {
```javascript
Ember.run.begin();
// code to be execute within a RunLoop
// code to be execute within a RunLoop
Ember.run.end();
```
@@ -6935,7 +6935,7 @@ Ember.String = {
'action_name'.classify(); // 'ActionName'
'css-class-name'.classify(); // 'CssClassName'
'my favorite items'.classify(); // 'MyFavoriteItems'
```
```
@method classify
@param {String} str the string to classify
@@ -7357,7 +7357,7 @@ Ember.Enumerable = Ember.Mixin.create(
@method nextObject
@param {Number} index the current index of the iteration
@param {Object} previousObject the value returned by the last call to
@param {Object} previousObject the value returned by the last call to
`nextObject`.
@param {Object} context a context object you can use to maintain state.
@return {Object} the next object in the iteration or undefined
@@ -8422,9 +8422,9 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
@method arrayContentWillChange
@param {Number} startIdx The starting index in the array that will change.
@param {Number} removeAmt The number of items that will be removed. If you
@param {Number} removeAmt The number of items that will be removed. If you
pass `null` assumes 0
@param {Number} addAmt The number of items that will be added If you
@param {Number} addAmt The number of items that will be added If you
pass `null` assumes 0.
@return {Ember.Array} receiver
*/
@@ -8898,11 +8898,11 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,
passed array. You should also call `this.enumerableContentDidChange()`
@method replace
@param {Number} idx Starting index in the array to replace. If
@param {Number} idx Starting index in the array to replace. If
idx >= length, then append to the end of the array.
@param {Number} amt Number of elements that should be removed from
@param {Number} amt Number of elements that should be removed from
the array, starting at *idx*.
@param {Array} objects An array of zero or more objects that should be
@param {Array} objects An array of zero or more objects that should be
inserted into the array at *idx*
*/
replace: Ember.required(),
@@ -10157,14 +10157,14 @@ CoreObject.PrototypeMixin = Mixin.create({
view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz']
```
Adding a single property that is not an array will just add it in the array:
```javascript
var view = App.FooBarView.create({
classNames: 'baz'
})
view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz']
```
Using the `concatenatedProperties` property, we can tell to Ember that mix
the content of the properties.
@@ -14249,7 +14249,7 @@ class:
* `mouseEnter`
* `mouseLeave`
Form events:
Form events:
* `submit`
* `change`
@@ -14257,7 +14257,7 @@ class:
* `focusOut`
* `input`
HTML5 drag and drop events:
HTML5 drag and drop events:
* `dragStart`
* `drag`
@@ -15794,14 +15794,14 @@ Ember.View.reopenClass({
`className` and optional `falsyClassName`.
- if a `className` or `falsyClassName` has been specified:
- if the value is truthy and `className` has been specified,
- if the value is truthy and `className` has been specified,
`className` is returned
- if the value is falsy and `falsyClassName` has been specified,
- if the value is falsy and `falsyClassName` has been specified,
`falsyClassName` is returned
- otherwise `null` is returned
- if the value is `true`, the dasherized last part of the supplied path
- if the value is `true`, the dasherized last part of the supplied path
is returned
- if the value is not `false`, `undefined` or `null`, the `value`
- if the value is not `false`, `undefined` or `null`, the `value`
is returned
- if none of the above rules apply, `null` is returned
@@ -16651,7 +16651,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
Given an empty `<body>` and the following code:
```javascript
```javascript
someItemsView = Ember.CollectionView.create({
classNames: ['a-collection'],
content: ['A','B','C'],
@@ -17823,7 +17823,7 @@ Ember.Handlebars.registerHelper('helperMissing', function(path, options) {
## Example with bound options
Bound hash options are also supported. Example:
Bound hash options are also supported. Example:
```handlebars
{{repeat text countBinding="numRepeats"}}
@@ -17861,15 +17861,15 @@ Ember.Handlebars.registerHelper('helperMissing', function(path, options) {
{{concatenate prop1 prop2 prop3}}. If any of the properties change,
the helpr will re-render. Note that dependency keys cannot be
using in conjunction with multi-property helpers, since it is ambiguous
which property the dependent keys would belong to.
which property the dependent keys would belong to.
## Use with unbound helper
The {{unbound}} helper can be used with bound helper invocations
The {{unbound}} helper can be used with bound helper invocations
to render them in their unbound form, e.g.
```handlebars
{{unbound capitalize name}}
{{unbound capitalize name}}
```
In this example, if the name property changes, the helper
@@ -17895,7 +17895,7 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) {
view = data.view,
currentContext = (options.contexts && options.contexts[0]) || this,
normalized,
pathRoot, path,
pathRoot, path,
loc, hashOption;
// Detect bound options (e.g. countBinding="otherCount")
@@ -18001,7 +18001,7 @@ function evaluateMultiPropertyBoundHelper(context, fn, normalizedProperties, opt
// Assemble liast of watched properties that'll re-render this helper.
watchedProperties = [];
for (boundOption in boundOptions) {
if (boundOptions.hasOwnProperty(boundOption)) {
if (boundOptions.hasOwnProperty(boundOption)) {
watchedProperties.push(normalizePath(context, boundOptions[boundOption], data));
}
}
@@ -18936,14 +18936,14 @@ EmberHandlebars.registerHelper('unless', function(context, options) {
Result in the following rendered output:
```html
```html
<img class="aValue">
```
A boolean return value will insert a specified class name if the property
returns `true` and remove the class name if the property returns `false`.
A class name is provided via the syntax
A class name is provided via the syntax
`somePropertyName:class-name-if-true`.
```javascript
@@ -19100,9 +19100,9 @@ EmberHandlebars.registerHelper('bindAttr', function(options) {
@method bindClasses
@for Ember.Handlebars
@param {Ember.Object} context The context from which to lookup properties
@param {String} classBindings A string, space-separated, of class bindings
@param {String} classBindings A string, space-separated, of class bindings
to use
@param {Ember.View} view The view in which observers should look for the
@param {Ember.View} view The view in which observers should look for the
element to update
@param {Srting} bindAttrId Optional bindAttr id used to lookup elements
@return {Array} An array of class names to add
@@ -19792,7 +19792,7 @@ Ember.Handlebars.registerHelper('unbound', function(property, fn) {
// Unbound helper call.
options.data.isUnbound = true;
helper = Ember.Handlebars.helpers[arguments[0]] || Ember.Handlebars.helperMissing;
out = helper.apply(this, Array.prototype.slice.call(arguments, 1));
out = helper.apply(this, Array.prototype.slice.call(arguments, 1));
delete options.data.isUnbound;
return out;
}
@@ -20087,7 +20087,7 @@ GroupedEach.prototype = {
```handlebars
{{#view App.MyView }}
{{each view.items itemViewClass="App.AnItemView"}}
{{each view.items itemViewClass="App.AnItemView"}}
{{/view}}
```
@@ -20118,7 +20118,7 @@ GroupedEach.prototype = {
<div class="ember-view">Greetings Sara</div>
</div>
```
### Representing each item with a Controller.
By default the controller lookup within an `{{#each}}` block will be
the controller of the template where the `{{#each}}` was used. If each
@@ -20126,10 +20126,10 @@ GroupedEach.prototype = {
`itemController` option which references a controller by lookup name.
Each item in the loop will be wrapped in an instance of this controller
and the item itself will be set to the `content` property of that controller.
This is useful in cases where properties of model objects need transformation
or synthesis for display:
```javascript
App.DeveloperController = Ember.ObjectController.extend({
isAvailableForHire: function(){
@@ -20137,13 +20137,13 @@ GroupedEach.prototype = {
}.property('isEmployed', 'isSeekingWork')
})
```
```handlebars
{{#each person in Developers itemController="developer"}}
{{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}}
{{/each}}
```
@method each
@for Ember.Handlebars.helpers
@param [name] {String} name for item (used with `in`)
@@ -21082,7 +21082,7 @@ helpers = helpers || Ember.Handlebars.helpers; data = data || {};
var buffer = '', stack1, hashTypes, escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) {
var buffer = '', hashTypes;
data.buffer.push("<option value=\"\">");
hashTypes = {};
@@ -21092,7 +21092,7 @@ function program1(depth0,data) {
}
function program3(depth0,data) {
var hashTypes;
hashTypes = {'contentBinding': "STRING"};
data.buffer.push(escapeExpression(helpers.view.call(depth0, "Ember.SelectOption", {hash:{
@@ -21107,7 +21107,7 @@ function program3(depth0,data) {
stack1 = helpers.each.call(depth0, "view.content", {hash:{},inverse:self.noop,fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashTypes:hashTypes,data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
return buffer;
}),
attributeBindings: ['multiple', 'disabled', 'tabindex'],
+80 -80
View File
@@ -184,99 +184,99 @@ performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
var $0 = $$.length - 1;
switch (yystate) {
case 1: return $$[$0-1];
case 1: return $$[$0-1];
break;
case 2: this.$ = new yy.ProgramNode([], $$[$0]);
case 2: this.$ = new yy.ProgramNode([], $$[$0]);
break;
case 3: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
case 3: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
break;
case 4: this.$ = new yy.ProgramNode($$[$0-1], []);
case 4: this.$ = new yy.ProgramNode($$[$0-1], []);
break;
case 5: this.$ = new yy.ProgramNode($$[$0]);
case 5: this.$ = new yy.ProgramNode($$[$0]);
break;
case 6: this.$ = new yy.ProgramNode([], []);
case 6: this.$ = new yy.ProgramNode([], []);
break;
case 7: this.$ = new yy.ProgramNode([]);
case 7: this.$ = new yy.ProgramNode([]);
break;
case 8: this.$ = [$$[$0]];
case 8: this.$ = [$$[$0]];
break;
case 9: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
case 9: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
break;
case 10: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
case 10: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
break;
case 11: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
case 11: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
break;
case 12: this.$ = $$[$0];
case 12: this.$ = $$[$0];
break;
case 13: this.$ = $$[$0];
case 13: this.$ = $$[$0];
break;
case 14: this.$ = new yy.ContentNode($$[$0]);
case 14: this.$ = new yy.ContentNode($$[$0]);
break;
case 15: this.$ = new yy.CommentNode($$[$0]);
case 15: this.$ = new yy.CommentNode($$[$0]);
break;
case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
break;
case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
break;
case 18: this.$ = $$[$0-1];
case 18: this.$ = $$[$0-1];
break;
case 19: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
case 19: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
break;
case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
break;
case 21: this.$ = new yy.PartialNode($$[$0-1]);
case 21: this.$ = new yy.PartialNode($$[$0-1]);
break;
case 22: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
case 22: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
break;
case 23:
case 23:
break;
case 24: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
case 24: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
break;
case 25: this.$ = [[$$[$0-1]].concat($$[$0]), null];
case 25: this.$ = [[$$[$0-1]].concat($$[$0]), null];
break;
case 26: this.$ = [[$$[$0-1]], $$[$0]];
case 26: this.$ = [[$$[$0-1]], $$[$0]];
break;
case 27: this.$ = [[$$[$0]], null];
case 27: this.$ = [[$$[$0]], null];
break;
case 28: this.$ = [[new yy.DataNode($$[$0])], null];
case 28: this.$ = [[new yy.DataNode($$[$0])], null];
break;
case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
break;
case 30: this.$ = [$$[$0]];
case 30: this.$ = [$$[$0]];
break;
case 31: this.$ = $$[$0];
case 31: this.$ = $$[$0];
break;
case 32: this.$ = new yy.StringNode($$[$0]);
case 32: this.$ = new yy.StringNode($$[$0]);
break;
case 33: this.$ = new yy.IntegerNode($$[$0]);
case 33: this.$ = new yy.IntegerNode($$[$0]);
break;
case 34: this.$ = new yy.BooleanNode($$[$0]);
case 34: this.$ = new yy.BooleanNode($$[$0]);
break;
case 35: this.$ = new yy.DataNode($$[$0]);
case 35: this.$ = new yy.DataNode($$[$0]);
break;
case 36: this.$ = new yy.HashNode($$[$0]);
case 36: this.$ = new yy.HashNode($$[$0]);
break;
case 37: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
case 37: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
break;
case 38: this.$ = [$$[$0]];
case 38: this.$ = [$$[$0]];
break;
case 39: this.$ = [$$[$0-2], $$[$0]];
case 39: this.$ = [$$[$0-2], $$[$0]];
break;
case 40: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
case 40: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
break;
case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
break;
case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
break;
case 43: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
case 43: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
break;
case 44: this.$ = new yy.PartialNameNode($$[$0]);
case 44: this.$ = new yy.PartialNameNode($$[$0]);
break;
case 45: this.$ = new yy.IdNode($$[$0]);
case 45: this.$ = new yy.IdNode($$[$0]);
break;
case 46: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
case 46: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
break;
case 47: this.$ = [$$[$0]];
case 47: this.$ = [$$[$0]];
break;
}
},
@@ -566,75 +566,75 @@ case 0:
if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
if(yy_.yytext) return 14;
break;
case 1: return 14;
case 1: return 14;
break;
case 2:
if(yy_.yytext.slice(-1) !== "\\") this.popState();
if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
return 14;
break;
case 3: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
case 3: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
break;
case 4: this.begin("par"); return 24;
case 4: this.begin("par"); return 24;
break;
case 5: return 16;
case 5: return 16;
break;
case 6: return 20;
case 6: return 20;
break;
case 7: return 19;
case 7: return 19;
break;
case 8: return 19;
case 8: return 19;
break;
case 9: return 23;
case 9: return 23;
break;
case 10: return 23;
case 10: return 23;
break;
case 11: this.popState(); this.begin('com');
case 11: this.popState(); this.begin('com');
break;
case 12: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
case 12: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
break;
case 13: return 22;
case 13: return 22;
break;
case 14: return 36;
case 14: return 36;
break;
case 15: return 35;
case 15: return 35;
break;
case 16: return 35;
case 16: return 35;
break;
case 17: return 39;
case 17: return 39;
break;
case 18: /*ignore whitespace*/
case 18: /*ignore whitespace*/
break;
case 19: this.popState(); return 18;
case 19: this.popState(); return 18;
break;
case 20: this.popState(); return 18;
case 20: this.popState(); return 18;
break;
case 21: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30;
case 21: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30;
break;
case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30;
case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30;
break;
case 23: yy_.yytext = yy_.yytext.substr(1); return 28;
case 23: yy_.yytext = yy_.yytext.substr(1); return 28;
break;
case 24: return 32;
case 24: return 32;
break;
case 25: return 32;
case 25: return 32;
break;
case 26: return 31;
case 26: return 31;
break;
case 27: return 35;
case 27: return 35;
break;
case 28: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35;
case 28: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35;
break;
case 29: return 'INVALID';
case 29: return 'INVALID';
break;
case 30: /*ignore whitespace*/
case 30: /*ignore whitespace*/
break;
case 31: this.popState(); return 37;
case 31: this.popState(); return 37;
break;
case 32: return 5;
case 32: return 5;
break;
}
};
+1 -1
View File
@@ -56,7 +56,7 @@ window.MessageBus = (function() {
callbacks: callbacks,
clientId: clientId,
stop: false,
start: function(opts) {
var poll,
_this = this;
+27 -27
View File
@@ -40,11 +40,11 @@ module MessageBus::Implementation
def logger
return @logger if @logger
require 'logger'
@logger = Logger.new(STDOUT)
@logger = Logger.new(STDOUT)
end
def sockets_enabled?
@sockets_enabled == false ? false : true
@sockets_enabled == false ? false : true
end
def sockets_enabled=(val)
@@ -68,13 +68,13 @@ module MessageBus::Implementation
end
def off
@off = true
@off = true
end
def on
@off = false
def on
@off = false
end
# Allow us to inject a redis db
def redis_config=(config)
@redis_config = config
@@ -114,10 +114,10 @@ module MessageBus::Implementation
end
def allow_broadcast?
@allow_broadcast ||=
@allow_broadcast ||=
if defined? ::Rails
::Rails.env.test? || ::Rails.env.development?
else
else
false
end
end
@@ -130,9 +130,9 @@ module MessageBus::Implementation
MessageBus::Diagnostics.enable
end
def publish(channel, data, opts = nil)
return if @off
def publish(channel, data, opts = nil)
return if @off
user_ids = nil
if opts
user_ids = opts[:user_ids] if opts
@@ -142,14 +142,14 @@ module MessageBus::Implementation
data: data,
user_ids: user_ids
})
reliable_pub_sub.publish(encode_channel_name(channel), encoded_data)
end
def blocking_subscribe(channel=nil, &blk)
if channel
reliable_pub_sub.subscribe(encode_channel_name(channel), &blk)
else
else
reliable_pub_sub.global_subscribe(&blk)
end
end
@@ -173,7 +173,7 @@ module MessageBus::Implementation
def subscribe(channel=nil, &blk)
subscribe_impl(channel, nil, &blk)
end
# subscribe only on current site
def local_subscribe(channel=nil, &blk)
site_id = MessageBus.site_id_lookup.call if MessageBus.site_id_lookup
@@ -181,15 +181,15 @@ module MessageBus::Implementation
end
def backlog(channel=nil, last_id)
old =
old =
if channel
reliable_pub_sub.backlog(encode_channel_name(channel), last_id)
else
else
reliable_pub_sub.global_backlog(encode_channel_name(channel), last_id)
end
old.each{ |m|
decode_message!(m)
decode_message!(m)
}
old
end
@@ -199,32 +199,32 @@ module MessageBus::Implementation
reliable_pub_sub.last_id(encode_channel_name(channel))
end
protected
protected
def decode_message!(msg)
channel, site_id = decode_channel_name(msg.channel)
msg.channel = channel
msg.channel = channel
msg.site_id = site_id
parsed = JSON.parse(msg.data)
parsed = JSON.parse(msg.data)
msg.data = parsed["data"]
msg.user_ids = parsed["user_ids"]
end
def subscribe_impl(channel, site_id, &blk)
@subscriptions ||= {}
@subscriptions ||= {}
@subscriptions[site_id] ||= {}
@subscriptions[site_id][channel] ||= []
@subscriptions[site_id][channel] << blk
@subscriptions[site_id][channel] ||= []
@subscriptions[site_id][channel] << blk
ensure_subscriber_thread
end
def ensure_subscriber_thread
@mutex ||= Mutex.new
@mutex.synchronize do
@mutex.synchronize do
return if @subscriber_thread
@subscriber_thread = Thread.new do
reliable_pub_sub.global_subscribe do |msg|
begin
begin
decode_message!(msg)
globals = @subscriptions[nil]
@@ -232,7 +232,7 @@ module MessageBus::Implementation
global_globals = globals[nil] if globals
local_globals = locals[nil] if locals
globals = globals[msg.channel] if globals
locals = locals[msg.channel] if locals
@@ -243,7 +243,7 @@ module MessageBus::Implementation
rescue => e
MessageBus.logger.warn "failed to process message #{msg.inspect}\n ex: #{e} backtrace: #{e.backtrace}"
end
end
end
end
+5 -5
View File
@@ -8,7 +8,7 @@ class MessageBus::Client
@subscriptions = {}
end
def close
def close
return unless @async_response
write_and_close "[]"
end
@@ -31,12 +31,12 @@ class MessageBus::Client
end
def subscriptions
@subscriptions
@subscriptions
end
def backlog
r = []
@subscriptions.each do |k,v|
@subscriptions.each do |k,v|
next if v.to_i < 0
messages = MessageBus.backlog(k,v)
messages.each do |msg|
@@ -46,8 +46,8 @@ class MessageBus::Client
end
# stats message for all newly subscribed
status_message = nil
@subscriptions.each do |k,v|
if v.to_i == -1
@subscriptions.each do |k,v|
if v.to_i == -1
status_message ||= {}
status_message[k] = MessageBus.last_id(k)
end
+10 -10
View File
@@ -4,23 +4,23 @@ class MessageBus::ConnectionManager
def initialize
@clients = {}
@subscriptions = {}
@subscriptions = {}
end
def notify_clients(msg)
begin
site_subs = @subscriptions[msg.site_id]
begin
site_subs = @subscriptions[msg.site_id]
subscription = site_subs[msg.channel] if site_subs
return unless subscription
subscription.each do |client_id|
client = @clients[client_id]
if client
allowed = !msg.user_ids || msg.user_ids.include?(client.user_id)
if allowed
if allowed
client << msg
# turns out you can delete from a set while itereating
# turns out you can delete from a set while itereating
remove_client(client)
end
end
@@ -29,7 +29,7 @@ class MessageBus::ConnectionManager
MessageBus.logger.error "notify clients crash #{e} : #{e.backtrace}"
end
end
def add_client(client)
@clients[client.client_id] = client
@subscriptions[client.site_id] ||= {}
@@ -51,8 +51,8 @@ class MessageBus::ConnectionManager
end
def subscribe_client(client,channel)
set = @subscriptions[client.site_id][channel]
unless set
set = @subscriptions[client.site_id][channel]
unless set
set = Set.new
@subscriptions[client.site_id][channel] = set
end
@@ -65,5 +65,5 @@ class MessageBus::ConnectionManager
subscriptions: @subscriptions
}
end
end
+7 -7
View File
@@ -1,15 +1,15 @@
class MessageBus::Diagnostics
def self.full_process_path
begin
info = `ps -eo "%p|$|%a" | grep '^\\s*#{Process.pid}'`
begin
info = `ps -eo "%p|$|%a" | grep '^\\s*#{Process.pid}'`
info.strip.split('|$|')[1]
rescue
# skip it ... not linux or something weird
end
end
def self.hostname
begin
begin
`hostname`.strip
rescue
# skip it
@@ -21,8 +21,8 @@ class MessageBus::Diagnostics
start_time = Time.now.to_f
hostname = self.hostname
# it may make sense to add a channel per machine/host to streamline
# process to process comms
# it may make sense to add a channel per machine/host to streamline
# process to process comms
MessageBus.subscribe('/_diagnostics/hup') do |msg|
if Process.pid == msg.data["pid"] && hostname == msg.data["hostname"]
$shutdown = true
@@ -33,7 +33,7 @@ class MessageBus::Diagnostics
MessageBus.subscribe('/_diagnostics/discover') do |msg|
MessageBus.on_connect.call msg.site_id if MessageBus.on_connect
MessageBus.publish '/_diagnostics/process-discovery', {
MessageBus.publish '/_diagnostics/process-discovery', {
pid: Process.pid,
process_name: $0,
full_path: full_path,
+2 -2
View File
@@ -1,5 +1,5 @@
class MessageBus::Message < Struct.new(:global_id, :message_id, :channel , :data)
attr_accessor :site_id, :user_ids
def self.decode(encoded)
@@ -10,7 +10,7 @@ class MessageBus::Message < Struct.new(:global_id, :message_id, :channel , :data
MessageBus::Message.new encoded[0..s1].to_i, encoded[s1+1..s2].to_i, encoded[s2+1..s3-1].gsub("$$123$$", "|"), encoded[s3+1..-1]
end
# only tricky thing to encode is pipes in a channel name ... do a straight replace
# only tricky thing to encode is pipes in a channel name ... do a straight replace
def encode
global_id.to_s << "|" << message_id.to_s << "|" << channel.gsub("|","$$123$$") << "|" << data
end
+2 -2
View File
@@ -8,13 +8,13 @@ class MessageBus::MessageHandler
def self.handle(name,&blk)
raise ArgumentError.new("expecting block") unless block_given?
raise ArgumentError.new("name") unless name
@@handlers ||= {}
@@handlers[name] = blk
end
def self.call(site_id, name, data, current_user_id)
begin
begin
MessageBus.on_connect.call(site_id) if MessageBus.on_connect
@@handlers[name].call(data,current_user_id)
ensure
@@ -57,14 +57,14 @@ HTML
# from ember-rails
def indent(string)
string.gsub(/$(.)/m, "\\1 ").strip
end
end
def call(env)
return @app.call(env) unless env['PATH_INFO'].start_with? '/message-bus/_diagnostics'
route = env['PATH_INFO'].split('/message-bus/_diagnostics')[1]
if MessageBus.is_admin_lookup.nil? || !MessageBus.is_admin_lookup.call(env)
return [403, {}, ['not allowed']]
end
@@ -85,14 +85,14 @@ HTML
asset = route.split('/assets/')[1]
if asset && !asset !~ /\//
content = asset_contents(asset)
content = asset_contents(asset)
split = asset.split('.')
if split[1] == 'handlebars'
content = translate_handlebars(split[0],content)
end
return [200, {'content-type' => 'text/javascript;'}, [content]]
end
return [404, {}, ['not found']]
end
end
+22 -22
View File
@@ -9,7 +9,7 @@ class MessageBus::Rack::Middleware
def self.start_listener
unless @started_listener
MessageBus.subscribe do |msg|
EM.next_tick do
EM.next_tick do
@@connection_manager.notify_clients(msg) if @@connection_manager
end
end
@@ -25,9 +25,9 @@ class MessageBus::Rack::Middleware
def self.backlog_to_json(backlog)
m = backlog.map do |msg|
{
{
:global_id => msg.global_id,
:message_id => msg.message_id,
:message_id => msg.message_id,
:channel => msg.channel,
:data => msg.data
}
@@ -36,7 +36,7 @@ class MessageBus::Rack::Middleware
end
def call(env)
return @app.call(env) unless env['PATH_INFO'] =~ /^\/message-bus/
# special debug/test route
@@ -46,7 +46,7 @@ class MessageBus::Rack::Middleware
return [200,{"Content-Type" => "text/html"},["sent"]]
end
if env['PATH_INFO'].start_with? '/message-bus/_diagnostics'
if env['PATH_INFO'].start_with? '/message-bus/_diagnostics'
diags = MessageBus::Rack::Diagnostics.new(@app)
return diags.call(env)
end
@@ -54,9 +54,9 @@ class MessageBus::Rack::Middleware
client_id = env['PATH_INFO'].split("/")[2]
return [404, {}, ["not found"]] unless client_id
user_id = MessageBus.user_id_lookup.call(env) if MessageBus.user_id_lookup
site_id = MessageBus.site_id_lookup.call(env) if MessageBus.site_id_lookup
user_id = MessageBus.user_id_lookup.call(env) if MessageBus.user_id_lookup
site_id = MessageBus.site_id_lookup.call(env) if MessageBus.site_id_lookup
client = MessageBus::Client.new(client_id: client_id, user_id: user_id, site_id: site_id)
connection = env['em.connection']
@@ -65,20 +65,20 @@ class MessageBus::Rack::Middleware
request.POST.each do |k,v|
client.subscribe(k, v)
end
backlog = client.backlog
headers = {}
headers["Cache-Control"] = "must-revalidate, private, max-age=0"
headers["Content-Type"] ="application/json; charset=utf-8"
if backlog.length > 0
if backlog.length > 0
[200, headers, [self.class.backlog_to_json(backlog)] ]
elsif MessageBus.long_polling_enabled? && env['QUERY_STRING'] !~ /dlp=t/
response = Thin::AsyncResponse.new(env)
response.headers["Cache-Control"] = "must-revalidate, private, max-age=0"
response.headers["Content-Type"] ="application/json; charset=utf-8"
response.status = 200
client.async_response = response
client.async_response = response
@@connection_manager.add_client(client)
@@ -86,16 +86,16 @@ class MessageBus::Rack::Middleware
client.close
@@connection_manager.remove_client(client)
}
throw :async
else
else
[200, headers, ["[]"]]
end
end
end
# there is also another in cramp this is from https://github.com/macournoyer/thin_async/blob/master/lib/thin/async.rb
# there is also another in cramp this is from https://github.com/macournoyer/thin_async/blob/master/lib/thin/async.rb
module Thin
unless defined?(DeferrableBody)
# Based on version from James Tucker <raggi@rubyforge.org>
@@ -115,7 +115,7 @@ module Thin
@body_callback = blk
schedule_dequeue
end
private
def schedule_dequeue
return unless @body_callback
@@ -129,14 +129,14 @@ module Thin
end
end
end
# Response whos body is sent asynchronously.
class AsyncResponse
include Rack::Response::Helpers
attr_reader :headers, :callback, :closed
attr_accessor :status
def initialize(env, status=200, headers={})
@callback = env['async.callback']
@body = DeferrableBody.new
@@ -144,25 +144,25 @@ module Thin
@headers = headers
@headers_sent = false
end
def send_headers
return if @headers_sent
@callback.call [@status, @headers, @body]
@headers_sent = true
end
def write(body)
send_headers
@body.call(body.respond_to?(:each) ? body : [body])
end
alias :<< :write
# Tell Thin the response is complete and the connection can be closed.
def done
@closed = true
send_headers
::EM.next_tick { @body.succeed }
end
end
end
+24 -24
View File
@@ -1,17 +1,17 @@
require 'redis'
# the heart of the message bus, it acts as 2 things
# the heart of the message bus, it acts as 2 things
#
# 1. A channel multiplexer
# 2. Backlog storage per-multiplexed channel.
# 2. Backlog storage per-multiplexed channel.
#
# ids are all sequencially increasing numbers starting at 0
# ids are all sequencially increasing numbers starting at 0
#
class MessageBus::ReliablePubSub
class NoMoreRetries < StandardError; end
class BackLogOutOfOrder < StandardError
class BackLogOutOfOrder < StandardError
attr_accessor :highest_id
def initialize(highest_id)
@@ -26,7 +26,7 @@ class MessageBus::ReliablePubSub
def max_publish_retries
@max_publish_retries ||= 10
end
def max_publish_wait=(ms)
@max_publish_wait = ms
end
@@ -35,11 +35,11 @@ class MessageBus::ReliablePubSub
@max_publish_wait ||= 500
end
# max_backlog_size is per multiplexed channel
# max_backlog_size is per multiplexed channel
def initialize(redis_config = {}, max_backlog_size = 1000)
@redis_config = redis_config
@max_backlog_size = 1000
# we can store a ton here ...
# we can store a ton here ...
@max_global_backlog_size = 100000
end
@@ -47,7 +47,7 @@ class MessageBus::ReliablePubSub
def max_global_backlog_size=(val)
@max_global_backlog_size = val
end
# per channel backlog size
def max_backlog_size=(val)
@max_backlog_size = val
@@ -62,7 +62,7 @@ class MessageBus::ReliablePubSub
"discourse_#{db}"
end
# redis connection used for publishing messages
# redis connection used for publishing messages
def pub_redis
@pub_redis ||= new_redis_connection
end
@@ -84,14 +84,14 @@ class MessageBus::ReliablePubSub
end
# use with extreme care, will nuke all of the data
def reset!
def reset!
pub_redis.keys("__mb_*").each do |k|
pub_redis.del k
end
end
def publish(channel, data)
redis = pub_redis
redis = pub_redis
backlog_id_key = backlog_id_key(channel)
backlog_key = backlog_key(channel)
@@ -126,13 +126,13 @@ class MessageBus::ReliablePubSub
end
def last_id(channel)
redis = pub_redis
redis = pub_redis
backlog_id_key = backlog_id_key(channel)
redis.get(backlog_id_key).to_i
end
def backlog(channel, last_id = nil)
redis = pub_redis
redis = pub_redis
backlog_key = backlog_key(channel)
items = redis.zrangebyscore backlog_key, last_id.to_i + 1, "+inf"
@@ -150,7 +150,7 @@ class MessageBus::ReliablePubSub
items.map! do |i|
pipe = i.index "|"
message_id = i[0..pipe].to_i
channel = i[pipe+1..-1]
channel = i[pipe+1..-1]
m = get_message(channel, message_id)
m
end
@@ -160,11 +160,11 @@ class MessageBus::ReliablePubSub
end
def get_message(channel, message_id)
redis = pub_redis
redis = pub_redis
backlog_key = backlog_key(channel)
items = redis.zrangebyscore backlog_key, message_id, message_id
if items && items[0]
if items && items[0]
MessageBus::Message.decode(items[0])
else
nil
@@ -172,8 +172,8 @@ class MessageBus::ReliablePubSub
end
def subscribe(channel, last_id = nil)
# trivial implementation for now,
# can cut down on connections if we only have one global subscriber
# trivial implementation for now,
# can cut down on connections if we only have one global subscriber
raise ArgumentError unless block_given?
if last_id
@@ -212,7 +212,7 @@ class MessageBus::ReliablePubSub
clear_backlog = lambda do
retries = 4
begin
begin
highest_id = process_global_backlog(highest_id, retries > 0, &blk)
rescue BackLogOutOfOrder => e
highest_id = e.highest_id
@@ -231,7 +231,7 @@ class MessageBus::ReliablePubSub
end
redis.subscribe(redis_channel_name) do |on|
on.subscribe do
on.subscribe do
if highest_id
clear_backlog.call(&blk)
end
@@ -242,12 +242,12 @@ class MessageBus::ReliablePubSub
# we have 2 options
#
# 1. message came in the correct order GREAT, just deal with it
# 2. message came in the incorrect order COMPLICATED, wait a tiny bit and clear backlog
# 2. message came in the incorrect order COMPLICATED, wait a tiny bit and clear backlog
if highest_id.nil? || m.global_id == highest_id + 1
highest_id = m.global_id
highest_id = m.global_id
yield m
else
else
clear_backlog.call(&blk)
end
end
+7 -7
View File
@@ -1,23 +1,23 @@
require 'spec_helper'
require 'message_bus'
describe MessageBus::Client do
describe MessageBus::Client do
describe "subscriptions" do
before do
@client = MessageBus::Client.new :client_id => 'abc'
end
it "should provide a list of subscriptions" do
it "should provide a list of subscriptions" do
@client.subscribe('/hello', nil)
@client.subscriptions['/hello'].should_not be_nil
@client.subscriptions['/hello'].should_not be_nil
end
it "should provide backlog for subscribed channel" do
it "should provide backlog for subscribed channel" do
@client.subscribe('/hello', nil)
MessageBus.publish '/hello', 'world'
log = @client.backlog
log = @client.backlog
log.length.should == 1
log[0].channel.should == '/hello'
log[0].data.should == 'world'
+12 -12
View File
@@ -2,7 +2,7 @@ require 'spec_helper'
require 'message_bus'
class FakeAsync
attr_accessor :cleanup_timer
def <<(val)
@@ -20,58 +20,58 @@ class FakeTimer
def cancel; @cancelled = true; end
end
describe MessageBus::ConnectionManager do
describe MessageBus::ConnectionManager do
before do
before do
@manager = MessageBus::ConnectionManager.new
@client = MessageBus::Client.new(client_id: "xyz", user_id: 1, site_id: 10)
@resp = FakeAsync.new
@client.async_response = @resp
@client.subscribe('test', -1)
@manager.add_client(@client)
@manager.add_client(@client)
@client.cleanup_timer = FakeTimer.new
end
it "should cancel the timer after its responds" do
it "should cancel the timer after its responds" do
m = MessageBus::Message.new(1,1,"test","data")
m.site_id = 10
@manager.notify_clients(m)
@client.cleanup_timer.cancelled.should == true
end
it "should be able to lookup an identical client" do
it "should be able to lookup an identical client" do
@manager.lookup_client(@client.client_id).should == @client
end
it "should be subscribed to a channel" do
it "should be subscribed to a channel" do
@manager.stats[:subscriptions][10]["test"].length == 1
end
it "should not notify clients on incorrect site" do
it "should not notify clients on incorrect site" do
m = MessageBus::Message.new(1,1,"test","data")
m.site_id = 9
@manager.notify_clients(m)
@resp.sent.should == nil
end
it "should notify clients on the correct site" do
it "should notify clients on the correct site" do
m = MessageBus::Message.new(1,1,"test","data")
m.site_id = 10
@manager.notify_clients(m)
@resp.sent.should_not == nil
end
it "should strip site id and user id from the payload delivered" do
it "should strip site id and user id from the payload delivered" do
m = MessageBus::Message.new(1,1,"test","data")
m.user_ids = [1]
m.site_id = 10
@manager.notify_clients(m)
parsed = JSON.parse(@resp.sent)
parsed = JSON.parse(@resp.sent)
parsed[0]["site_id"].should == nil
parsed[0]["user_id"].should == nil
end
it "should not deliver unselected" do
it "should not deliver unselected" do
m = MessageBus::Message.new(1,1,"test","data")
m.user_ids = [5]
m.site_id = 10
+13 -13
View File
@@ -5,17 +5,17 @@ require 'redis'
describe MessageBus do
before do
MessageBus.site_id_lookup do
before do
MessageBus.site_id_lookup do
"magic"
end
MessageBus.redis_config = {}
end
it "should automatically decode hashed messages" do
it "should automatically decode hashed messages" do
data = nil
MessageBus.subscribe("/chuck") do |msg|
data = msg.data
MessageBus.subscribe("/chuck") do |msg|
data = msg.data
end
MessageBus.publish("/chuck", {:norris => true})
wait_for(1000){ data }
@@ -23,10 +23,10 @@ describe MessageBus do
data["norris"].should == true
end
it "should get a message if it subscribes to it" do
it "should get a message if it subscribes to it" do
@data,@site_id,@channel = nil
MessageBus.subscribe("/chuck") do |msg|
MessageBus.subscribe("/chuck") do |msg|
@data = msg.data
@site_id = msg.site_id
@channel = msg.channel
@@ -36,7 +36,7 @@ describe MessageBus do
MessageBus.publish("/chuck", "norris", user_ids: [1,2,3])
wait_for(1000){@data}
@data.should == 'norris'
@site_id.should == 'magic'
@channel.should == '/chuck'
@@ -45,10 +45,10 @@ describe MessageBus do
end
it "should get global messages if it subscribes to them" do
it "should get global messages if it subscribes to them" do
@data,@site_id,@channel = nil
MessageBus.subscribe do |msg|
MessageBus.subscribe do |msg|
@data = msg.data
@site_id = msg.site_id
@channel = msg.channel
@@ -57,7 +57,7 @@ describe MessageBus do
MessageBus.publish("/chuck", "norris")
wait_for(1000){@data}
@data.should == 'norris'
@site_id.should == 'magic'
@channel.should == '/chuck'
+5 -5
View File
@@ -3,14 +3,14 @@ require 'message_bus'
describe MessageBus::MessageHandler do
it "should properly register message handlers" do
it "should properly register message handlers" do
MessageBus::MessageHandler.handle "/hello" do |m|
m
end
MessageBus::MessageHandler.call("site","/hello", "world", 1).should == "world"
end
it "should correctly load message handlers" do
it "should correctly load message handlers" do
MessageBus::MessageHandler.load_handlers("#{File.dirname(__FILE__)}/handlers")
MessageBus::MessageHandler.call("site","/dupe", "1", 1).should == "11"
end
@@ -19,7 +19,7 @@ describe MessageBus::MessageHandler do
MessageBus::MessageHandler.handle "/channel" do |m|
m
end
connected = false
disconnected = false
@@ -31,9 +31,9 @@ describe MessageBus::MessageHandler do
end
MessageBus::MessageHandler.call("site_id", "/channel", "data", 1)
connected.should == true
disconnected.should == true
end
end
+15 -15
View File
@@ -7,7 +7,7 @@ describe MessageBus::Rack::Middleware do
class FakeAsyncMiddleware
def self.in_async?
def self.in_async?
@@in_async if defined? @@in_async
end
@@ -20,7 +20,7 @@ describe MessageBus::Rack::Middleware do
EM.run {
env['async.callback'] = lambda { |r|
# more judo with deferrable body, at this point we just have headers
r[2].callback do
r[2].callback do
# even more judo cause rack test does not call each like the spec says
body = ""
r[2].each do |m|
@@ -35,7 +35,7 @@ describe MessageBus::Rack::Middleware do
}
EM::Timer.new(1) { EM.stop }
defer = lambda {
if !result
@@in_async = true
@@ -63,7 +63,7 @@ describe MessageBus::Rack::Middleware do
end
describe "long polling" do
before do
before do
MessageBus.sockets_enabled = false
MessageBus.long_polling_enabled = true
end
@@ -83,7 +83,7 @@ describe MessageBus::Rack::Middleware do
parsed[0]["data"]["/foo"].should == MessageBus.last_id("/foo")
end
it "should respond to long polls when data is available" do
it "should respond to long polls when data is available" do
Thread.new do
wait_for(2000) { FakeAsyncMiddleware.in_async? }
@@ -98,7 +98,7 @@ describe MessageBus::Rack::Middleware do
parsed[0]["data"].should == "bar"
end
it "should timeout within its alloted slot" do
it "should timeout within its alloted slot" do
begin
MessageBus.long_polling_interval = 10
s = Time.now.to_f * 1000
@@ -110,9 +110,9 @@ describe MessageBus::Rack::Middleware do
end
end
describe "diagnostics" do
describe "diagnostics" do
it "should return a 403 if a user attempts to get at the _diagnostics path" do
it "should return a 403 if a user attempts to get at the _diagnostics path" do
get "/message-bus/_diagnostics"
last_response.status.should == 403
end
@@ -131,9 +131,9 @@ describe MessageBus::Rack::Middleware do
end
end
describe "polling" do
before do
before do
MessageBus.sockets_enabled = false
MessageBus.long_polling_enabled = false
end
@@ -149,8 +149,8 @@ describe MessageBus::Rack::Middleware do
last_response.should be_ok
end
it "should correctly understand that -1 means stuff from now onwards" do
it "should correctly understand that -1 means stuff from now onwards" do
MessageBus.publish('foo', 'bar')
post "/message-bus/ABCD", {
@@ -164,7 +164,7 @@ describe MessageBus::Rack::Middleware do
end
it "should respond with the data if messages exist in the backlog" do
it "should respond with the data if messages exist in the backlog" do
id = MessageBus.last_id('/foo')
MessageBus.publish("/foo", "barbs")
@@ -182,10 +182,10 @@ describe MessageBus::Rack::Middleware do
parsed[1]["data"].should == "borbs"
end
it "should not get consumed messages" do
it "should not get consumed messages" do
MessageBus.publish("/foo", "barbs")
id = MessageBus.last_id('/foo')
client_id = "ABCD"
post "/message-bus/#{client_id}", {
'/foo' => id
+5 -5
View File
@@ -28,12 +28,12 @@ describe MessageBus::ReliablePubSub do
end
end
it 'gets every response from child processes' do
it 'gets every response from child processes' do
pid = nil
Redis.new(:db => 10).flushall
begin
pids = (1..10).map{spawn_child}
responses = []
pids = (1..10).map{spawn_child}
responses = []
bus = MessageBus::ReliablePubSub.new(:db => 10)
Thread.new do
bus.subscribe("/response", 0) do |msg|
@@ -41,7 +41,7 @@ describe MessageBus::ReliablePubSub do
end
end
10.times{bus.publish("/echo", Process.pid.to_s)}
wait_for 4000 do
wait_for 4000 do
responses.count == 100
end
@@ -50,7 +50,7 @@ describe MessageBus::ReliablePubSub do
responses.count.should == 100
ensure
if pids
pids.each do |pid|
pids.each do |pid|
Process.kill("HUP", pid)
Process.wait(pid)
end
+33 -33
View File
@@ -7,22 +7,22 @@ describe MessageBus::ReliablePubSub do
MessageBus::ReliablePubSub.new(:db => 10)
end
before do
before do
@bus = new_test_bus
@bus.reset!
end
it "should be able to access the backlog" do
it "should be able to access the backlog" do
@bus.publish "/foo", "bar"
@bus.publish "/foo", "baz"
@bus.backlog("/foo", 0).to_a.should == [
@bus.backlog("/foo", 0).to_a.should == [
MessageBus::Message.new(1,1,'/foo','bar'),
MessageBus::Message.new(2,2,'/foo','baz')
]
end
it "should truncate channels correctly" do
it "should truncate channels correctly" do
@bus.max_backlog_size = 2
4.times do |t|
@bus.publish "/foo", t.to_s
@@ -34,18 +34,18 @@ describe MessageBus::ReliablePubSub do
]
end
it "should be able to grab a message by id" do
it "should be able to grab a message by id" do
id1 = @bus.publish "/foo", "bar"
id2 = @bus.publish "/foo", "baz"
@bus.get_message("/foo", id2).should == MessageBus::Message.new(2, 2, "/foo", "baz")
@bus.get_message("/foo", id1).should == MessageBus::Message.new(1, 1, "/foo", "bar")
end
it "should be able to access the global backlog" do
it "should be able to access the global backlog" do
@bus.publish "/foo", "bar"
@bus.publish "/hello", "world"
@bus.publish "/hello", "world"
@bus.publish "/foo", "baz"
@bus.publish "/hello", "planet"
@bus.publish "/hello", "planet"
@bus.global_backlog.to_a.should == [
MessageBus::Message.new(1, 1, "/foo", "bar"),
@@ -55,7 +55,7 @@ describe MessageBus::ReliablePubSub do
]
end
it "should correctly omit dropped messages from the global backlog" do
it "should correctly omit dropped messages from the global backlog" do
@bus.max_backlog_size = 1
@bus.publish "/foo", "a"
@bus.publish "/foo", "b"
@@ -68,12 +68,12 @@ describe MessageBus::ReliablePubSub do
]
end
it "should have the correct number of messages for multi threaded access" do
it "should have the correct number of messages for multi threaded access" do
threads = []
4.times do
threads << Thread.new do
bus = new_test_bus
25.times {
4.times do
threads << Thread.new do
bus = new_test_bus
25.times {
bus.publish "/foo", "."
}
end
@@ -86,14 +86,14 @@ describe MessageBus::ReliablePubSub do
it "should be able to subscribe globally with recovery" do
@bus.publish("/foo", "1")
@bus.publish("/bar", "2")
got = []
got = []
t = Thread.new do
new_test_bus.global_subscribe(0) do |msg|
t = Thread.new do
new_test_bus.global_subscribe(0) do |msg|
got << msg
end
end
@bus.publish("/bar", "3")
wait_for(100) do
@@ -101,29 +101,29 @@ describe MessageBus::ReliablePubSub do
end
t.kill
got.length.should == 3
got.map{|m| m.data}.should == ["1","2","3"]
end
it "should be able to encode and decode messages properly" do
it "should be able to encode and decode messages properly" do
m = MessageBus::Message.new 1,2,'||','||'
MessageBus::Message.decode(m.encode).should == m
end
it "should handle subscribe on single channel, with recovery" do
it "should handle subscribe on single channel, with recovery" do
@bus.publish("/foo", "1")
@bus.publish("/bar", "2")
got = []
got = []
t = Thread.new do
new_test_bus.subscribe("/foo",0) do |msg|
t = Thread.new do
new_test_bus.subscribe("/foo",0) do |msg|
got << msg
end
end
@bus.publish("/foo", "3")
wait_for(100) do
got.length == 2
end
@@ -133,22 +133,22 @@ describe MessageBus::ReliablePubSub do
got.map{|m| m.data}.should == ["1","3"]
end
it "should not get backlog if subscribe is called without params" do
it "should not get backlog if subscribe is called without params" do
@bus.publish("/foo", "1")
got = []
got = []
t = Thread.new do
new_test_bus.subscribe("/foo") do |msg|
t = Thread.new do
new_test_bus.subscribe("/foo") do |msg|
got << msg
end
end
# sleep 50ms to allow the bus to correctly subscribe,
# sleep 50ms to allow the bus to correctly subscribe,
# I thought about adding a subscribed callback, but outside of testing it matters less
sleep 0.05
@bus.publish("/foo", "2")
wait_for(100) do
got.length == 1
end
@@ -158,7 +158,7 @@ describe MessageBus::ReliablePubSub do
got.map{|m| m.data}.should == ["2"]
end
it "should allow us to get last id on a channel" do
it "should allow us to get last id on a channel" do
@bus.last_id("/foo").should == 0
@bus.publish("/foo", "1")
@bus.last_id("/foo").should == 1
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:test) do |spec|
spec.pattern = 'spec/*_spec.rb'
@@ -14,7 +14,7 @@ module RailsMultisite
handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
@@connection_handlers[spec] = handler
end
else
else
handler = @@default_connection_handler
end
ActiveRecord::Base.connection_handler = handler
@@ -30,25 +30,25 @@ module RailsMultisite
yield db
ActiveRecord::Base.connection_handler.clear_active_connections!
end
establish_connection(:db => old)
establish_connection(:db => old)
ActiveRecord::Base.connection_handler.clear_active_connections! unless connected
end
def self.all_dbs
["default"] +
["default"] +
if defined?(@@db_spec_cache) && @@db_spec_cache
@@db_spec_cache.keys.to_a
else
else
[]
end
end
def self.current_db
db = ActiveRecord::Base.connection_pool.spec.config[:db_key] || "default"
db = ActiveRecord::Base.connection_pool.spec.config[:db_key] || "default"
end
def self.config_filename=(config_filename)
@@config_filename = config_filename
@@config_filename = config_filename
end
def self.config_filename
@@ -90,8 +90,8 @@ module RailsMultisite
@@host_spec_cache[host] = @@default_spec
end
# inject our connection_handler pool
# WARNING MONKEY PATCH
# inject our connection_handler pool
# WARNING MONKEY PATCH
#
# see: https://github.com/rails/rails/issues/8344#issuecomment-10800848
#
@@ -103,7 +103,7 @@ module RailsMultisite
module NewConnectionHandler
def self.included(klass)
klass.class_eval do
klass.class_eval do
define_singleton_method :connection_handler do
Thread.current[:connection_handler] || @connection_handler
end
@@ -122,7 +122,7 @@ module RailsMultisite
def call(env)
request = Rack::Request.new(env)
begin
begin
#TODO: add a callback so users can simply go to a domain to register it, or something
return [404, {}, ["not found"]] unless @@host_spec_cache[request.host]
@@ -139,9 +139,9 @@ module RailsMultisite
if opts[:host]
@@host_spec_cache[opts[:host]]
else
@@db_spec_cache[opts[:db]]
@@db_spec_cache[opts[:db]]
end
end
end
end
+1 -1
View File
@@ -16,6 +16,6 @@ module RailsMultisite
end
end
end
end
+4 -4
View File
@@ -4,17 +4,17 @@ task "multisite:generate:config" => :environment do
if File.exists?(filename)
puts "Config is already generated at #{RailsMultisite::ConnectionManagement::CONFIG_FILE}"
else
else
puts "Generated config file at #{RailsMultisite::ConnectionManagement::CONFIG_FILE}"
File.open(filename, 'w') do |f|
File.open(filename, 'w') do |f|
f.write <<-CONFIG
# site_name:
# site_name:
# adapter: postgresql
# database: db_name
# host: localhost
# pool: 5
# timeout: 5000
# db_id: 1 # optionally include other settings you need
# db_id: 1 # optionally include other settings you need
# host_names:
# - www.mysite.com
# - www.anothersite.com
@@ -6,7 +6,7 @@ describe RailsMultisite::ConnectionManagement do
include Rack::Test::Methods
def app
RailsMultisite::ConnectionManagement.config_filename = 'spec/fixtures/two_dbs.yml'
RailsMultisite::ConnectionManagement.load_settings!
@@ -18,7 +18,7 @@ describe RailsMultisite::ConnectionManagement do
}.to_app
end
after do
after do
RailsMultisite::ConnectionManagement.clear_settings!
end
@@ -31,12 +31,12 @@ describe RailsMultisite::ConnectionManagement do
get 'http://second.localhost/html'
last_response.should be_ok
end
it 'returns 200 for valid main site' do
get 'http://default.localhost/html'
last_response.should be_ok
end
it 'returns 404 for invalid site' do
get '/html'
last_response.should be_not_found
@@ -24,7 +24,7 @@ describe RailsMultisite::ConnectionManagement do
before do
subject.config_filename = "spec/fixtures/two_dbs.yml"
subject.load_settings!
end
end
its(:all_dbs) { should == ['default', 'second']}
context 'second db' do
+3 -3
View File
@@ -1,9 +1,9 @@
second:
second:
adapter: sqlite3
database: second_db
username: username
password: password
db_id: 1
db_id: 1
host_names:
- second.localhost
- 2nd.localhost
- 2nd.localhost
+1 -1
View File
@@ -7,7 +7,7 @@ ENV["RAILS_ENV"] ||= 'test'
RSpec.configure do |config|
config.color_enabled = true
config.before(:suite) do
ActiveRecord::Base.configurations['test'] = (YAML::load(File.open("spec/fixtures/database.yml"))['test'])
end