thunderer 0.9.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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +16 -0
  6. data/Guardfile +17 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +32 -0
  9. data/Rakefile +12 -0
  10. data/app/assets/javascripts/thunderer.js +93 -0
  11. data/app/assets/javascripts/thunderer_angular.js +43 -0
  12. data/lib/generators/thunderer/install_generator.rb +19 -0
  13. data/lib/generators/thunderer/templates/thunderer.ru +10 -0
  14. data/lib/generators/thunderer/templates/thunderer.yml +10 -0
  15. data/lib/thunderer/controller_additions.rb +38 -0
  16. data/lib/thunderer/engine.rb +20 -0
  17. data/lib/thunderer/faye_extension.rb +40 -0
  18. data/lib/thunderer/messanger.rb +50 -0
  19. data/lib/thunderer/parser.rb +22 -0
  20. data/lib/thunderer/publish_changes.rb +51 -0
  21. data/lib/thunderer/railtie.rb +15 -0
  22. data/lib/thunderer/version.rb +3 -0
  23. data/lib/thunderer/view_helpers.rb +22 -0
  24. data/lib/thunderer.rb +66 -0
  25. data/spec/fixtures/thunderer.yml +8 -0
  26. data/spec/javascripts/helpers/.gitkeep +0 -0
  27. data/spec/javascripts/helpers/jasmine_examples/SpecHelper.js +15 -0
  28. data/spec/javascripts/support/jasmine.yml +123 -0
  29. data/spec/javascripts/support/jasmine_helper.rb +11 -0
  30. data/spec/javascripts/thunderer_interceptor_spec.js +0 -0
  31. data/spec/javascripts/thunderer_spec.js +118 -0
  32. data/spec/javascripts/thunderer_subscription_service_spec.js +0 -0
  33. data/spec/spec_helper.rb +29 -0
  34. data/spec/support/controller_macros.rb +5 -0
  35. data/spec/support/matchers/have_filter.rb +6 -0
  36. data/spec/thunderer/controller_aditions_spec.rb +48 -0
  37. data/spec/thunderer/messanger_spec.rb +46 -0
  38. data/spec/thunderer/parser_spec.rb +35 -0
  39. data/spec/thunderer/publish_changes_spec.rb +0 -0
  40. data/spec/thunderer_spec.rb +108 -0
  41. data/thunderer.gemspec +30 -0
  42. metadata +198 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eed2e6107ca366aa72173a03e02aac6315d21a98
4
+ data.tar.gz: d1700a9f0bf3511b4be6ba4b5d5e8d3411e10e8a
5
+ SHA512:
6
+ metadata.gz: 74e671d789053ceb11bfa8e579978d2c38c3d9fad54d889e312885f8324056c247f560bbcabd6d9a1400bc4fcacf46649abab795d7095a9ec8e07033c92d5454
7
+ data.tar.gz: 1a8414e53918afbb5e9f83f36f99b979c573418064ca962db96d631c09527fe21bfc20c69d2dccf13c607959ee5840b92e7c553d8636fee67011bd749174fd26
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/
19
+ .rbenv-version
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ addons:
7
+ code_climate:
8
+ repo_token:
9
+ secure: kQfmRpllfvpVjss75SRQKDJ70j72doORRsr8EQyY3cJsoAnwfu8E25/JZ+ZoG25AS+Z6dNYpSH0unrsqzbaSGucFfnQD0TiGINJhs36svHhtrP+JDSxwE38PyKu3tX07TMDxfZ0ig8OfuEuB3RB+E/1/1fy4RrrDn+ZIQN0dIHs=
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in thunderer.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'guard-rspec', require: false
8
+ gem 'guard-jasmine'
9
+ gem 'rsense'
10
+ end
11
+
12
+
13
+ group :test do
14
+ gem 'rspec'
15
+ gem 'codeclimate-test-reporter', require: nil
16
+ end
data/Guardfile ADDED
@@ -0,0 +1,17 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' , cmd: 'bundle exec rspec'do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { 'spec' }
8
+
9
+ end
10
+
11
+
12
+ # guard :jasmine do
13
+ # watch(%r{spec/javascripts/spec\.(js\.coffee|js|coffee)$}) { 'spec/javascripts' }
14
+ # watch(%r{spec/javascripts/.+_spec\.(js\.coffee|js|coffee)$})
15
+ # watch(%r{spec/javascripts/fixtures/.+$})
16
+ # watch(%r{app/assets/javascripts/(.+?)\.(js\.coffee|js|coffee)(?:\.\w+)*$}) { |m| "spec/javascripts/#{ m[1] }_spec.#{ m[2] }" }
17
+ # end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Nick Chubarov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ [![Build Status](https://travis-ci.org/chubarovNick/thunderer.svg?branch=master)](https://travis-ci.org/chubarovNick/thunderer)
2
+ [![Code Climate](https://codeclimate.com/github/chubarovNick/thunderer.png)](https://codeclimate.com/github/chubarovNick/thunderer)
3
+ [![Dependency Status](https://gemnasium.com/chubarovNick/thunderer.svg)](https://gemnasium.com/chubarovNick/thunderer)
4
+ # Thunderer
5
+
6
+ Thunderer is gem for publishing messages through [Faye](http://faye.jcoglan.com/). It allows you to easily provide real-time updates through an open socket without tying
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'thunderer'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install thunderer
21
+
22
+ ## Usage
23
+
24
+ TODO: Write usage instructions here
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it ( http://github.com/<my-github-username>/thunderer/fork )
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Run RSpec'
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.verbose = false
8
+ end
9
+
10
+ task default: :spec
11
+ require 'jasmine'
12
+ load 'jasmine/tasks/jasmine.rake'
@@ -0,0 +1,93 @@
1
+ var Thunderer = (function (doc) {
2
+ var handleResponse, connectToFaye, self;
3
+
4
+ self = {
5
+ connecting: false,
6
+ fayeClient: null,
7
+ fayeCallbacks: [],
8
+ subscriptions: {},
9
+ subscriptionObjects: {},
10
+ subscriptionCallbacks: {}
11
+ };
12
+
13
+ handleResponse = function(message) {
14
+ if (callback = self.subscriptionCallbacks[message.channel]) {
15
+ callback(message.data, message.channel);
16
+ }
17
+ };
18
+
19
+ connectToFaye = function () {
20
+ self.fayeClient = new Faye.Client(self.subscriptions.server);
21
+ self.fayeClient.addExtension(self.fayeExtension);
22
+ for (var i=0; i < self.fayeCallbacks.length; i++) {
23
+ self.fayeCallbacks[i](self.fayeClient);
24
+ };
25
+ };
26
+
27
+ self.sign = function(options) {
28
+ if (!self.subscriptions.server) {
29
+ self.subscriptions.server = options.server;
30
+ }
31
+ self.subscriptions[options.channel] = options;
32
+ self.faye(function(faye) {
33
+ var sub = faye.subscribe(options.channel, handleResponse);
34
+ self.subscriptionObjects[options.channel] = sub;
35
+ if (options.subscription) {
36
+ options.subscription(sub);
37
+ }
38
+ });
39
+ };
40
+
41
+ self.subscribe = function (channel, callback) {
42
+ self.subscriptionCallbacks[channel] = callback;
43
+ };
44
+
45
+ self.faye = function (callback) {
46
+ if (self.fayeClient) {
47
+ callback(self.fayeClient);
48
+ } else {
49
+ self.fayeCallbacks.push(callback);
50
+ if (self.subscriptions.server && !self.connecting) {
51
+ self.connecting = true;
52
+ if (typeof Faye === 'undefined') {
53
+ console.log('Faye is undefined, you should require faye.js before using Thunderer')
54
+ } else {
55
+ connectToFaye();
56
+ }
57
+ }
58
+ }
59
+ };
60
+
61
+ self.fayeExtension ={
62
+ outgoing: function(message, callback) {
63
+ if (message.channel == "/meta/subscribe") {
64
+ // Attach the signature and timestamp to subscription messages
65
+ var subscription = self.subscriptions[message.subscription];
66
+ if (!message.ext) message.ext = {};
67
+ message.ext.thunderer_signature = subscription.signature;
68
+ message.ext.thunderer_timestamp = subscription.timestamp;
69
+ }
70
+ callback(message);
71
+ }
72
+ };
73
+
74
+ self.subscription = function(channel) {
75
+ return self.subscriptionObjects[channel];
76
+ };
77
+ self.unsubscribe = function (channel) {
78
+ var sub = self.subscription(channel);
79
+ if (sub) {
80
+ sub.cancel();
81
+ delete self.subscriptionObjects[channel];
82
+ }
83
+ };
84
+
85
+ self.unsubscribeAll = function () {
86
+ for (var i in self.subscriptionObjects) {
87
+ if ( self.subscriptionObjects.hasOwnProperty(i) ) {
88
+ self.unsubscribe(i);
89
+ }
90
+ }
91
+ };
92
+ return self;
93
+ }(document));
@@ -0,0 +1,43 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ angular.module('Thunderer', [])
5
+
6
+ .factory('ThundererInterceptor', function ($q) {
7
+ var self = {
8
+ response: function (response) {
9
+ var rawChanels = response.headers().channels;
10
+ if (rawChanels) {
11
+ var channels = JSON.parse(rawChanels);
12
+ for (var i = 0; i < channels.length; i++) {
13
+ Thunderer.sign(channels[i]);
14
+ }
15
+ }
16
+ return response;
17
+ },
18
+ responseError: function (rejection) {
19
+ $q.reject(rejection);
20
+ }
21
+ };
22
+
23
+ return self;
24
+
25
+ })
26
+
27
+ .service('$thunderer', function () {
28
+
29
+ var self = {
30
+ addListener: function (channel, callback) {
31
+ Thunderer.subscribe(channel, callback);
32
+ },
33
+ removeListener: function (channel) {
34
+ Thunderer.unsubscribe(channel)
35
+ },
36
+ removeAllListners: function () {
37
+ Thunderer.unsubscribeAll()
38
+ }
39
+ }
40
+ return self;
41
+ })
42
+
43
+ }());
@@ -0,0 +1,19 @@
1
+ module Thunderer
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ def self.source_root
5
+ File.dirname(__FILE__) + '/templates'
6
+ end
7
+
8
+ def copy_files
9
+ template 'thunderer.yml', 'config/thunderer.yml'
10
+ if ::Rails.version < '3.1'
11
+ copy_file '../../../../app/assets/javascripts/thunderer.js', 'public/javascripts/thunderer.js'
12
+ copy_file '../../../../app/assets/javascripts/thunderer_interceptor.js', 'public/javascripts/thunderer_interceptor.js'
13
+ copy_file '../../../../app/assets/javascripts/thunderer_subscription_service.js', 'public/javascripts/thunderer_subscription_service.js'
14
+ end
15
+ copy_file 'thunderer.ru', 'thunderer.ru'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ # Run with: rackup thunderer.ru -s thin -E production
2
+ require 'bundler/setup'
3
+ require 'yaml'
4
+ require 'faye'
5
+ require 'thunderer'
6
+
7
+ Faye::WebSocket.load_adapter('thin')
8
+
9
+ Thunderer.load_config(File.expand_path('../config/thunderer.yml', __FILE__), ENV['RAILS_ENV'] || 'development')
10
+ run Thunderer.faye_app
@@ -0,0 +1,10 @@
1
+ development:
2
+ server: "http://localhost:9292/faye"
3
+ secret_token: "secret"
4
+ test:
5
+ server: "http://localhost:9292/faye"
6
+ secret_token: "secret"
7
+ production:
8
+ server: "http://example.com/faye"
9
+ secret_token: "<%= defined?(SecureRandom) ? SecureRandom.hex(32) : ActiveSupport::SecureRandom.hex(32) %>"
10
+ signature_expiration: 3600 # one hour
@@ -0,0 +1,38 @@
1
+ require 'active_support'
2
+ module Thunderer
3
+ module ControllerAdditions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ cattr_accessor :channels, :interpolation_object
8
+
9
+ before_action :add_channels_header,only: [:index]
10
+
11
+ private
12
+
13
+ def add_channels_header
14
+ headers['channels'] = (self.class.channels || []).map do |channel|
15
+ new_str = if self.class.interpolation_object && channel
16
+ object = send(self.class.interpolation_object)
17
+ Thunderer::Parser.interpolate_channel channel, object
18
+ else
19
+ channel
20
+ end
21
+ Thunderer.subscription(channel: new_str)
22
+ end.to_json
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+
28
+ def thunderer_channels(*args)
29
+ options = args.extract_options!
30
+ options.assert_valid_keys(:object)
31
+ self.interpolation_object = options[:object]
32
+ self.channels = Array.wrap(args)
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ require 'thunderer/view_helpers'
2
+ module Thunderer
3
+ class Engine < ::Rails::Engine
4
+
5
+ initializer "thunderer.config" do
6
+ path = Rails.root.join("config/thunderer.yml")
7
+ Thunderer.load_config(path, Rails.env) if path.exist?
8
+ end
9
+
10
+ initializer :assets do |config|
11
+ Rails.application.config.assets.paths << File.join(
12
+ Gem.loaded_specs['faye'].full_gem_path, 'lib')
13
+ end
14
+
15
+ initializer "thunderer.view_helpers" do
16
+ ActionView::Base.send :include, ViewHelpers
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ module Thunderer
2
+ # This class is an extension for the Faye::RackAdapter.
3
+ # It is used inside of Thunderer.faye_app.
4
+ class FayeExtension
5
+ # Callback to handle incoming Faye messages. This authenticates both
6
+ # subscribe and publish calls.
7
+ def incoming(message, callback)
8
+ if message["channel"] == "/meta/subscribe"
9
+ authenticate_subscribe(message)
10
+ elsif message["channel"] !~ %r{^/meta/}
11
+ authenticate_publish(message)
12
+ end
13
+ callback.call(message)
14
+ end
15
+
16
+ private
17
+
18
+ # Ensure the subscription signature is correct and that it has not expired.
19
+ def authenticate_subscribe(message)
20
+ subscription = Thunderer.subscription(:channel => message["subscription"], :timestamp => message["ext"]["thunderer_timestamp"])
21
+ if message["ext"]["thunderer_signature"] != subscription[:signature]
22
+ message["error"] = "Incorrect signature."
23
+ elsif Thunderer.signature_expired? message["ext"]["thunderer_timestamp"].to_i
24
+ message["error"] = "Signature has expired."
25
+ end
26
+ end
27
+
28
+ # Ensures the secret token is correct before publishing.
29
+ def authenticate_publish(message)
30
+ if Thunderer.config[:secret_token].nil?
31
+ raise Error, "No secret_token config set, ensure thunderer.yml is loaded properly."
32
+ elsif message["ext"]["thunderer_secret_token"] != Thunderer.config[:secret_token]
33
+
34
+ message["error"] = "Incorrect token."
35
+ else
36
+ message["ext"]["thunderer_secret_token"] = nil
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ module Thunderer
2
+ module Messanger
3
+ class ConfigurationError < StandardError; end
4
+
5
+ class << self
6
+ attr_reader :config
7
+
8
+ def reset_config
9
+ @config = {}
10
+ end
11
+
12
+ def configure url
13
+ reset_config
14
+ uri = URI.parse(url)
15
+ @config['uri'] = uri
16
+ @config['use_ssl'] = uri.scheme == 'https'
17
+ end
18
+
19
+ def post message
20
+ raise ConfigurationError if not_configured?
21
+
22
+ form = build_form
23
+ form.set_form_data(:message => message.to_json)
24
+ protocol.start {|h| h.request(form)}
25
+ end
26
+
27
+ private
28
+
29
+ def build_form
30
+ uri = @config['uri']
31
+ Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
32
+ end
33
+
34
+ def protocol
35
+ uri = @config['uri']
36
+ http = Net::HTTP.new(uri.host, uri.port)
37
+ http.use_ssl = @config['use_ssl']
38
+ if @config['use_ssl']
39
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
40
+ end
41
+ http
42
+ end
43
+
44
+ def not_configured?
45
+ @config == {}
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+ module Thunderer
2
+ module Parser
3
+
4
+ class << self
5
+ def interpolate_channel channel, object
6
+ channel.gsub(/:\w*\b/, interpolation_hash(channel, object))
7
+ end
8
+
9
+ private
10
+
11
+ def interpolation_hash channel, object
12
+ {}.tap do |result|
13
+ channel.scan(/:\w*\b/).map do |interpolation_key|
14
+ object_method = interpolation_key.gsub(':', '')
15
+ replaced_string = object.send(object_method).to_s
16
+ result[interpolation_key] = replaced_string
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ require 'active_support'
2
+ module Thunderer
3
+ module PublishChanges
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ after_save :publish_changes
8
+
9
+ class << self
10
+ attr_accessor :channels, :options, :block
11
+ end
12
+
13
+ private
14
+
15
+ def publish_changes
16
+ (self.class.channels || []).each do |channel|
17
+ rooted_message = if message_root?
18
+ Hash[message_root, notification_message]
19
+ else
20
+ notification_message
21
+ end
22
+ Thunderer.publish_to Thunderer::Parser.interpolate_channel(channel, self), rooted_message
23
+ end
24
+ end
25
+
26
+ def notification_message
27
+ block = self.class.block
28
+ block ? block.call(self) : self
29
+ end
30
+
31
+ def message_root
32
+ self.class.options[:message_root]
33
+ end
34
+
35
+ def message_root?
36
+ message_root.present?
37
+ end
38
+
39
+ end
40
+
41
+ module ClassMethods
42
+ def notify_client_to_channels *args, &block
43
+ @options = args.extract_options!
44
+ @options.assert_valid_keys(:message_root)
45
+ @channels = args
46
+ @block = block
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ module Thunderer
2
+ class Railtie < Rails::Railtie
3
+ initializer 'thunderer.controller' do
4
+ ActiveSupport.on_load(:action_controller) do
5
+ include Thunderer::ControllerAdditions
6
+ end
7
+ end
8
+
9
+ initializer 'thunderer.active_record' do
10
+ ActiveSupport.on_load(:active_record) do
11
+ include Thunderer::PublishChanges
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Thunderer
2
+ VERSION = '0.9.0'
3
+ end
@@ -0,0 +1,22 @@
1
+ module Thunderer
2
+ module ViewHelpers
3
+ # Publish the given data or block to the client by sending
4
+ # a Net::HTTP POST request to the Faye server. If a block
5
+ # or string is passed in, it is evaluated as JavaScript
6
+ # on the client. Otherwise it will be converted to JSON
7
+ # for use in a JavaScript callback.
8
+ def publish_to(channel, data = nil, &block)
9
+ Thunderer.publish_to(channel, data || capture(&block))
10
+ end
11
+
12
+ # Subscribe the client to the given channel. This generates
13
+ # some JavaScript calling PrivatePub.sign with the subscription
14
+ # options.
15
+ def subscribe_to(channel)
16
+ subscription = Thunderer.subscription(:channel => channel)
17
+ content_tag "script", :type => "text/javascript" do
18
+ raw("Thunderer.sign(#{subscription.to_json});")
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/thunderer.rb ADDED
@@ -0,0 +1,66 @@
1
+ require "thunderer/version"
2
+ require 'thunderer/parser'
3
+ require 'thunderer/messanger'
4
+ require 'thunderer/publish_changes'
5
+ require 'thunderer/faye_extension'
6
+ require "digest/sha1"
7
+ require "net/http"
8
+ require "net/https"
9
+ require 'yaml'
10
+ require "thunderer/engine" if defined? Rails
11
+
12
+ module Thunderer
13
+ class Error < StandardError; end
14
+
15
+ class << self
16
+ attr_reader :config
17
+ attr_reader :messanger
18
+
19
+ def reset_config
20
+ @config = {}
21
+ end
22
+
23
+ def load_config filename, environment
24
+ reset_config
25
+ config_yaml = YAML.load_file(filename)[environment]
26
+ raise ArgumentError, "The #{environment} environment dose not exist" unless config_yaml
27
+ config_yaml.each { |k,v| config[k.to_sym] = v }
28
+ Thunderer::Messanger.configure(config[:server])
29
+ @messanger = Thunderer::Messanger
30
+
31
+ end
32
+
33
+ def publish_to channel, data
34
+ publish_message(message(channel, data))
35
+ end
36
+
37
+ def publish_message(message)
38
+ raise Error, "No server specified, ensure private_pub.yml was loaded properly." unless config[:server]
39
+ url = URI.parse(config[:server])
40
+ messanger.post(message)
41
+ end
42
+
43
+ def message(channel, data)
44
+ {:channel => channel,
45
+ :data => {
46
+ :channel => channel,
47
+ :data => data},
48
+ :ext => {:thunderer_secret_token => config[:secret_token]}}
49
+ end
50
+
51
+ def subscription(options = {})
52
+ sub = {:server => config[:server], :timestamp => (Time.now.to_f * 1000).round}.merge(options)
53
+ sub[:signature] = Digest::SHA1.hexdigest([config[:secret_token], sub[:channel], sub[:timestamp]].join)
54
+ sub
55
+ end
56
+
57
+ def signature_expired?(timestamp)
58
+ timestamp < ((Time.now.to_f - config[:signature_expiration])*1000).round if config[:signature_expiration]
59
+ end
60
+
61
+ def faye_app(options = {})
62
+ options = {mount: '/faye', timeout: 45, extensions: [FayeExtension.new] }.merge(options)
63
+ Faye::RackAdapter.new(options)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,8 @@
1
+ development:
2
+ server: http://dev.local:9292/faye
3
+ secret_token: DEVELOPMENT_SECRET_TOKEN
4
+ signature_expiration: 600
5
+ production:
6
+ server: http://example.com/faye
7
+ secret_token: PRODUCTION_SECRET_TOKEN
8
+ signature_expiration: 600
File without changes