<commit_before>NoDevent
=======
A system for notifying browser clients about events happening in a Ruby On Rails (or other non-evented backend) such as private messages, new discussion posts, or interesting resque jobs.  The goal is to reduce or elliminate the need for polling from the browser.  

See a demo at http://nodevent.com

# Motivation

Both node.js and Rails are great systems.  I think rails is great for its maturity and feature set, and Node.js is great for socket.io.  There are times when I'm working in a Rails app, and I want some kind of socket.io interaction with the browser.  With NoDevent I can write all of my application logic in Rails, and syndicate the socket.io events out to the browser.  With careful organization of NoDevent events you can extend the View of MVC into the browser and have confidence that that when a Model changes, all Views of it change too.  

Quick* Start (Rails)
-----------
1) Install Node.js (http://nodejs.org/)

2) Install and start NoDevent with npm:

    npm install nodevent
    npm start nodevent

3) Add the following to the gemspec

    gem 'nodevent'

nodevent expects a $redis global to exist and be a connect to your redis server.

4) Send some events

ActiveRecord Models:
```ruby
class SomeModel < ActiveRecord::Base
  include NoDevent::Base
end

#Emit a model instance as a json object to that model
SomeModel.first.emit('the_event', SomeModel.first)

#Or just emit a message
NoDevent::Emitter.emit('the_room', 'the_event', 'the_message')

#You can also use an active record model as the room.
NoDevent::Emitter.emit(SomeModel.first, 'the_event', 'the_message')
```

3) Connect and listen for events in the browser
Load the socket.io layer:
```
<%= javascript_include_nodevent %>
```

Connect to a room and listen for events
```javascript
  var room = NoDevent.room('theroom');
  room.join();
  theroom.on("the_event",function(data){
    /* Do stuff with data */
  });
  
```

Thats it!

*Ok, so it's not that quick.


# Documentation

## Appliance

The NoDevent server appliance is provided as an npm module/server.  Simply install the 'nodevent' module and you can 'npn start nodevent' to get started.  The appliance is stateless with respect to you rapplication, so you can run as many instances as you like.

### Configuration

NoDevent is configured via /etc/nodevent.json.  If you are running from source you can also provide the file to read as a command line parameter ('node server.js other_config.json').

An example config file.
```javascript
{
  "port" : 8080,
  "/nodevent" : { 
    "redis" : {"port" :6379 ,"host" : "localhost"}
  }
}
```
The above config us what is used by default if no config file is available.  It tells NoDevent to listen on port 8080, and have one namespace.  Each Namespace listens to a distinct redis instance for events.  Each namespace may also provide a secret for security.

You may provide a top-level ssl : {key : 'keyfile', cert: 'certfile'} if you want to use ssl.

A config with every option being used
```javascript
{
  "port" : 443,
  "ssl" : {
    "key" : "keyfilename",
    "cert" : "certfilename"
  }
  "/nodevent" : { 
    "redis" : {"port" :6379 ,"host" : "localhost"}
  }
  "/other_namespace" : { 
    "redis" : {"port" :6379 ,"host" : "redis.internal"},
    "secret" : "<KEY>"
  }
}
```

### Security
It is possible to secure access to rooms with a secret key.  If a secret string has been provided for a room that room is secured and any client that wishes to join must provide a valid key.

A valid key is generated by:
```javascript
sha256(room + ts + secret)
```
where 'room' is the name of the room, ts is the experation time of the key, and secret is the configured secret.


## Web

The web (javscript) component of NoDevent is for receiving events.  

### Connecting

```html
<script src='//localhost:8080/api/nodevent' type='text/javascript'></script
```

The above loads the required js, and initiates a socket.io connection to the '/nodevent' namespace.  After this script tag, the NoDevent system is ready to go.

### NoDevent object

```javascript
window.NoDevent
```

You do not need to wait to use this object.  It will be there after the script tag has executed and it's ready to go.

#### Events
To get notified of events happening to the socket connection used by NoDevent you may listen for events directly on the window.NoDevent object:

```javascript
// Called when a connection is established to the server
NoDevent.on('connect', function(){});

// Called when disconnected from the server
NoDevent.on('disconnect', function(){});
```

For more events, see https://github.com/LearnBoost/socket.io-client/#events

You can also ask NoDevent directly if it is connected with the connected() method.

```javascript
Nodevent.connected()
```

####  Rooms

To get events you must first get a room.

```javascript
var room = NoDevent.room('the_room')
```

The room object returned is where you manage all interactions with a room

##### Methods and Properties

Setting the key for a room (see security section above)
```javascript
room.setKey(key)
```

Joining a room:
```javascript
room.join(function(err){});
```
Initiates a request to join a room.  If a callback is provided it will be called a single time, with an err if the join failed.

Leaving a room
```javascript
room.leave(function(err){});
```
Initiates a request to leave a room.  If a callback is provided it will be called a single time, with an err if leaving the room failed.

If all you have is the room object you can get the room's name
```javascript
room.id;
```

##### Events

Every room object is an event emitter with two special events:

```javascript
room.on('join', function(err){})
```
This event is emitted every time a join to the room happens, or is attempted.  The err object will exist if there was an error joining the room

```javascript
room.on('leave', function(err){})
```
This event is emitted every time you leave the room.  The err object will exist if the leave was unexpected and indicate what happened.

To listen to an event that you sent from a backend process simply wait for the named event
```javascript
room.on('the_event', function(data){})
```

The data variable will have whatever was passed (if anything).

## Ruby gem

The ruby (gem) component of NoDevent is for sending events.  

### Installation

There is a rubygem named 'nodevent'.  To install simply `gem install nodevent` the in ruby:
```ruby
require 'nodevent'
```

or, add this to your Gemfile
```ruby
gem 'nodevent'
```

This will get you a NoDevent module.

#### Configuring NoDevent

The NoDevent module comes with a default config that will work if you're using the default appliance config.  NoDevent expects a global $redis redis connection to exist, and it will use that to post events.

You can override that config
```ruby
NoDevent::Emitter.config = {:host => "http://myawesomesite", :namespace => "/other_namespace", :secret => '<KEY>' }
```
The namespace must match exactly to your configured namespace in the appliance.  If there is a missmatch nothing will happen in the client and it can be VERY frustrating to debug.  The secret must match also, or you will get errors attempting to join a room.  You are also completely replacing the default config, so you must specify everything.


### View helper
To get the magic javascript include tag in your view there is a rails view helper.  
```ruby
javascript_include_nodevent
```
That will include the script tag with the configured path. 

### NoDevent::Emitter

This is where the magic happens.  

####Example usage:
In the controller
```ruby
@room = NoDevent::Emitter.room(current_user)
@roomkey = NoDevent::Emitter.room_key(current_user, Time.zone.now + 1.hour)
```

In the view
```erb
<%= content_for :javascript do %>
  current_user_room = NoDevent.room('<%=@room%>');
  current_user_room.setKey('<%=@roomkey%>');
  current_user_room.join();
<% end %>

### NoDevent::Emitter.room(obj)

Example usage:
```ruby
# Get the name of a room for a class/module
@roomname = NoDevent::Emitter.room(SomeModel)

# Get the name of a room for a model instance
@roomname = NoDevent::Emitter.room(SomeModel.first)

# Just returns "the_room"
@roomname = NoDevent::Emitter.room("the_room")
```
This is a helper method to get the name of a room.  You don't really need to use it, but it can convert objects that inherit from ActiveRecord::Base into their unique room name.

### NoDevent::Emitter.room_key(obj, expires)

This is a helper method to generate the key for a room with a given experation time.  

Example usage:
```ruby
@roomkey = NoDevent::Emitter.room_key(SomeModel, Time.zone.now + 1.hour)
@roomkey = NoDevent::Emitter.room_key(SomeModel.first, Time.zone.now + 1.hour)
@roomkey = NoDevent::Emitter.room_key(@roomname, Time.zone.now + 1.hour)
@roomkey = NoDevent::Emitter.room_key('the_room', Time.zone.now + 1.hour)
```
The above code will generate a key for the room that is valid for the next hour.

### NoDevent::Emitter.emit(room, event, message)

This method emits a named event with a message (any object that responds to .to_json) to the room.

```ruby
NoDevent::Emitter.emit(NoDevent::Emitter.room(SomeModel.first), 'the_event', 'some_message')
NoDevent::Emitter.emit('the_room', 'the_event', {:some => :data, :other => :thing})
```

The event will travel through redis to the NoDevent appliance server, and any browser client room object will emit an event named after the event name.
```javascript
var room = Nodevent.room('the_room')
room.join()
room.on('the_event', function(message) {});
```

This method can be called from anywhere, and it will send the event to any browser client listening appropriately.  This means it can be emitted on model creation, or even from a resque job.

### NoDevent mixin.
To help even further, I've provided the NoDevent::Base module as an include-able thing for models.

```ruby
class SomeModel < ActiveRecord::Base
  include NoDevent::Base

  after_create :nodevent_create
  after_update :nodevent_update

  def as_json(options={})
    super(options).merge(:nodevent => {:room => room, :key => room_key(Time.zone.now + 1.hour)})
  end

end
```

The above example includes NoDevent support directly into a model.

It also provides the following:
* The class's room will be notified of created instances
* Instances will include the room name and key for getting updates
* Updates to a model will notify listeners of the change


When using this you can ask SomeModel for its room name
```ruby
@roomname = SomeMode.first.room
```

or the room key
```ruby
@roomkey = SomeModel.first.room_key(Time.zone.now + 1.hour)
```

or even emit directly to it's room
```ruby
# Emit an arbitrary message to the model instance's room
SomeModel.first.emit('the_event', 'some_message')

# Emit a json-ed version of the model
SomeModel.first.emit('update')
```

## Versioning

I'm using the following versioning scheme:
```
x.y.z
```

A change in the 'x' indictes that there has been a backwards incompatible interface change.  A change in the 'y' indicates some new functionality or significant bug fix, and a change in the 'z' indictes a minor bug fix.  

Don't attempt to get a guage on how mature this system is by the version numbers.  I'm following this pattern so that automatic version matching systems like bundler and npm can update developers' packages and protect against backwards incompatible changes. The rubygem and npm package follow each other in version number on the 'x', and 'y', but not 'z'.
<commit_msg>derp?<commit_after>NoDevent
=======
A system for notifying browser clients about events happening in a Ruby On Rails (or other non-evented backend) such as private messages, new discussion posts, or interesting resque jobs.  The goal is to reduce or elliminate the need for polling from the browser.  

See a demo at http://nodevent.com

# Motivation

Both node.js and Rails are great systems.  I think rails is great for its maturity and feature set, and Node.js is great for socket.io.  There are times when I'm working in a Rails app, and I want some kind of socket.io interaction with the browser.  With NoDevent I can write all of my application logic in Rails, and syndicate the socket.io events out to the browser.  With careful organization of NoDevent events you can extend the View of MVC into the browser and have confidence that that when a Model changes, all Views of it change too.  

Quick* Start (Rails)
-----------
1) Install Node.js (http://nodejs.org/)


2) Install and start NoDevent with npm:

    npm install nodevent
    npm start nodevent

3) Add the following to the gemspec

    gem 'nodevent'

nodevent expects a $redis global to exist and be a connect to your redis server.

4) Send some events

ActiveRecord Models:
```ruby
class SomeModel < ActiveRecord::Base
  include NoDevent::Base
end

#Emit a model instance as a json object to that model
SomeModel.first.emit('the_event', SomeModel.first)

#Or just emit a message
NoDevent::Emitter.emit('the_room', 'the_event', 'the_message')

#You can also use an active record model as the room.
NoDevent::Emitter.emit(SomeModel.first, 'the_event', 'the_message')
```

3) Connect and listen for events in the browser
Load the socket.io layer:
```
<%= javascript_include_nodevent %>
```

Connect to a room and listen for events
```javascript
  var room = NoDevent.room('theroom');
  room.join();
  theroom.on("the_event",function(data){
    /* Do stuff with data */
  });
  
```

Thats it!

*Ok, so it's not that quick.


# Documentation

## Appliance

The NoDevent server appliance is provided as an npm module/server.  Simply install the 'nodevent' module and you can 'npn start nodevent' to get started.  The appliance is stateless with respect to you rapplication, so you can run as many instances as you like.

### Configuration

NoDevent is configured via /etc/nodevent.json.  If you are running from source you can also provide the file to read as a command line parameter ('node server.js other_config.json').

An example config file.
```javascript
{
  "port" : 8080,
  "/nodevent" : { 
    "redis" : {"port" :6379 ,"host" : "localhost"}
  }
}
```
The above config us what is used by default if no config file is available.  It tells NoDevent to listen on port 8080, and have one namespace.  Each Namespace listens to a distinct redis instance for events.  Each namespace may also provide a secret for security.

You may provide a top-level ssl : {key : 'keyfile', cert: 'certfile'} if you want to use ssl.

A config with every option being used
```javascript
{
  "port" : 443,
  "ssl" : {
    "key" : "keyfilename",
    "cert" : "certfilename"
  }
  "/nodevent" : { 
    "redis" : {"port" :6379 ,"host" : "localhost"}
  }
  "/other_namespace" : { 
    "redis" : {"port" :6379 ,"host" : "redis.internal"},
    "secret" : "<KEY>"
  }
}
```

### Security
It is possible to secure access to rooms with a secret key.  If a secret string has been provided for a room that room is secured and any client that wishes to join must provide a valid key.

A valid key is generated by:
```javascript
sha256(room + ts + secret)
```
where 'room' is the name of the room, ts is the experation time of the key, and secret is the configured secret.


## Web

The web (javscript) component of NoDevent is for receiving events.  

### Connecting

```html
<script src='//localhost:8080/api/nodevent' type='text/javascript'></script
```

The above loads the required js, and initiates a socket.io connection to the '/nodevent' namespace.  After this script tag, the NoDevent system is ready to go.

### NoDevent object

```javascript
window.NoDevent
```

You do not need to wait to use this object.  It will be there after the script tag has executed and it's ready to go.

#### Events
To get notified of events happening to the socket connection used by NoDevent you may listen for events directly on the window.NoDevent object:

```javascript
// Called when a connection is established to the server
NoDevent.on('connect', function(){});

// Called when disconnected from the server
NoDevent.on('disconnect', function(){});
```

For more events, see https://github.com/LearnBoost/socket.io-client/#events

You can also ask NoDevent directly if it is connected with the connected() method.

```javascript
Nodevent.connected()
```

####  Rooms

To get events you must first get a room.

```javascript
var room = NoDevent.room('the_room')
```

The room object returned is where you manage all interactions with a room

##### Methods and Properties

Setting the key for a room (see security section above)
```javascript
room.setKey(key)
```

Joining a room:
```javascript
room.join(function(err){});
```
Initiates a request to join a room.  If a callback is provided it will be called a single time, with an err if the join failed.

Leaving a room
```javascript
room.leave(function(err){});
```
Initiates a request to leave a room.  If a callback is provided it will be called a single time, with an err if leaving the room failed.

If all you have is the room object you can get the room's name
```javascript
room.id;
```

##### Events

Every room object is an event emitter with two special events:

```javascript
room.on('join', function(err){})
```
This event is emitted every time a join to the room happens, or is attempted.  The err object will exist if there was an error joining the room

```javascript
room.on('leave', function(err){})
```
This event is emitted every time you leave the room.  The err object will exist if the leave was unexpected and indicate what happened.

To listen to an event that you sent from a backend process simply wait for the named event
```javascript
room.on('the_event', function(data){})
```

The data variable will have whatever was passed (if anything).

## Ruby gem

The ruby (gem) component of NoDevent is for sending events.  

### Installation

There is a rubygem named 'nodevent'.  To install simply `gem install nodevent` the in ruby:
```ruby
require 'nodevent'
```

or, add this to your Gemfile
```ruby
gem 'nodevent'
```

This will get you a NoDevent module.

#### Configuring NoDevent

The NoDevent module comes with a default config that will work if you're using the default appliance config.  NoDevent expects a global $redis redis connection to exist, and it will use that to post events.

You can override that config
```ruby
NoDevent::Emitter.config = {:host => "http://myawesomesite", :namespace => "/other_namespace", :secret => '<KEY>' }
```
The namespace must match exactly to your configured namespace in the appliance.  If there is a missmatch nothing will happen in the client and it can be VERY frustrating to debug.  The secret must match also, or you will get errors attempting to join a room.  You are also completely replacing the default config, so you must specify everything.


### View helper
To get the magic javascript include tag in your view there is a rails view helper.  
```ruby
javascript_include_nodevent
```
That will include the script tag with the configured path. 

### NoDevent::Emitter

This is where the magic happens.  

####Example usage:
In the controller
```ruby
@room = NoDevent::Emitter.room(current_user)
@roomkey = NoDevent::Emitter.room_key(current_user, Time.zone.now + 1.hour)
```

In the view
```erb
<%= content_for :javascript do %>
  current_user_room = NoDevent.room('<%=@room%>');
  current_user_room.setKey('<%=@roomkey%>');
  current_user_room.join();
<% end %>

### NoDevent::Emitter.room(obj)

Example usage:
```ruby
# Get the name of a room for a class/module
@roomname = NoDevent::Emitter.room(SomeModel)

# Get the name of a room for a model instance
@roomname = NoDevent::Emitter.room(SomeModel.first)

# Just returns "the_room"
@roomname = NoDevent::Emitter.room("the_room")
```
This is a helper method to get the name of a room.  You don't really need to use it, but it can convert objects that inherit from ActiveRecord::Base into their unique room name.

### NoDevent::Emitter.room_key(obj, expires)

This is a helper method to generate the key for a room with a given experation time.  

Example usage:
```ruby
@roomkey = NoDevent::Emitter.room_key(SomeModel, Time.zone.now + 1.hour)
@roomkey = NoDevent::Emitter.room_key(SomeModel.first, Time.zone.now + 1.hour)
@roomkey = NoDevent::Emitter.room_key(@roomname, Time.zone.now + 1.hour)
@roomkey = NoDevent::Emitter.room_key('the_room', Time.zone.now + 1.hour)
```
The above code will generate a key for the room that is valid for the next hour.

### NoDevent::Emitter.emit(room, event, message)

This method emits a named event with a message (any object that responds to .to_json) to the room.

```ruby
NoDevent::Emitter.emit(NoDevent::Emitter.room(SomeModel.first), 'the_event', 'some_message')
NoDevent::Emitter.emit('the_room', 'the_event', {:some => :data, :other => :thing})
```

The event will travel through redis to the NoDevent appliance server, and any browser client room object will emit an event named after the event name.
```javascript
var room = Nodevent.room('the_room')
room.join()
room.on('the_event', function(message) {});
```

This method can be called from anywhere, and it will send the event to any browser client listening appropriately.  This means it can be emitted on model creation, or even from a resque job.

### NoDevent mixin.
To help even further, I've provided the NoDevent::Base module as an include-able thing for models.

```ruby
class SomeModel < ActiveRecord::Base
  include NoDevent::Base

  after_create :nodevent_create
  after_update :nodevent_update

  def as_json(options={})
    super(options).merge(:nodevent => {:room => room, :key => room_key(Time.zone.now + 1.hour)})
  end

end
```

The above example includes NoDevent support directly into a model.

It also provides the following:
* The class's room will be notified of created instances
* Instances will include the room name and key for getting updates
* Updates to a model will notify listeners of the change


When using this you can ask SomeModel for its room name
```ruby
@roomname = SomeMode.first.room
```

or the room key
```ruby
@roomkey = SomeModel.first.room_key(Time.zone.now + 1.hour)
```

or even emit directly to it's room
```ruby
# Emit an arbitrary message to the model instance's room
SomeModel.first.emit('the_event', 'some_message')

# Emit a json-ed version of the model
SomeModel.first.emit('update')
```

## Versioning

I'm using the following versioning scheme:
```
x.y.z
```

A change in the 'x' indictes that there has been a backwards incompatible interface change.  A change in the 'y' indicates some new functionality or significant bug fix, and a change in the 'z' indictes a minor bug fix.  

Don't attempt to get a guage on how mature this system is by the version numbers.  I'm following this pattern so that automatic version matching systems like bundler and npm can update developers' packages and protect against backwards incompatible changes. The rubygem and npm package follow each other in version number on the 'x', and 'y', but not 'z'.

{'fraction_non_alphanumeric': 0.09719991317560235, 'fraction_numerical': 0.003646624701541133, 'mean_word_length': 4.342300556586271, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 35, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1710332', 'n_tokens_mistral': 6920, 'n_tokens_neox': 6554, 'n_words': 3114}