hookly 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/LICENSE.md +22 -0
- data/README.md +75 -0
- data/hookly.gemspec +23 -0
- data/lib/error.rb +1 -0
- data/lib/errors/response_error.rb +7 -0
- data/lib/hookly/version.rb +8 -0
- data/lib/hookly.rb +31 -0
- data/lib/message.rb +18 -0
- data/lib/request.rb +35 -0
- data/spec/helpers/response_helper.rb +3 -0
- data/spec/helpers/tokens_helper.rb +3 -0
- data/spec/lib/channel_spec.rb +0 -0
- data/spec/lib/message_spec.rb +50 -0
- data/spec/lib/request_spec.rb +90 -0
- data/spec/spec_helper.rb +28 -0
- data/vendor/assets/javascripts/hookly.js +7143 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8fa80ff4c39928e6d5e8a0a6f789c7346d0da23f
|
4
|
+
data.tar.gz: e751e4408f4e131e8a9856d2315b10ff21ba359f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7bb848c28200fbfe0fc742091f34b8196dcfd2100189fa16c609e6dede135ae7295c15883c073861993b8e713e4b93680303d2c4f4671d6243ae53395e68aef5
|
7
|
+
data.tar.gz: cc8ae03b69889979098c526bce2da03b5f8c9164ef2f1646df0ad218085ae7ab235e8b4b69d11df3d88b007a2712e0179c07408fe689c895f3dfe153afbabfec
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Brian Norton
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#hookly-rails
|
2
|
+
|
3
|
+
1. Ruby wrapper for the Hookly API
|
4
|
+
2. hookly.js asset pipeline provider/wrapper
|
5
|
+
|
6
|
+
Rails 3.1+ asset-pipeline gem to provide hookly.js
|
7
|
+
|
8
|
+
#Setup
|
9
|
+
|
10
|
+
Add to your Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'hookly-rails'
|
14
|
+
```
|
15
|
+
|
16
|
+
###JS Setup
|
17
|
+
Then add this to you application.js manifest:
|
18
|
+
|
19
|
+
```javascript
|
20
|
+
//= require hookly
|
21
|
+
```
|
22
|
+
|
23
|
+
Then check out the [hookly.js docs](https://github.com/bnorton/hookly.js) for usage and working examples
|
24
|
+
|
25
|
+
###API Setup
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
# config/initializers/hookly.rb
|
29
|
+
Hookly.token = '{{token}}'
|
30
|
+
Hookly.secret = '{{secret}}'
|
31
|
+
```
|
32
|
+
|
33
|
+
###Post a message to the #updates channel
|
34
|
+
|
35
|
+
In the client javascript subscribe to '#updates'
|
36
|
+
```javascript
|
37
|
+
hookly.setup('{{token}}')
|
38
|
+
hookly.on('#updates', function(options) {
|
39
|
+
// options == { model: 'Message', id: 5, text: 'Thanks for the info.' }
|
40
|
+
});
|
41
|
+
```
|
42
|
+
|
43
|
+
The push a new message the updates channel
|
44
|
+
```ruby
|
45
|
+
Hookly::Channel.new('#updates').push(model: 'Message', id: 5, text: 'Thanks for the info.')
|
46
|
+
#=> #<Hookly::Message id: '44fjwq-djas' slug: '#updates', data: { model: 'Message', id: 5, text: 'Thanks for the info.' }>
|
47
|
+
```
|
48
|
+
|
49
|
+
###Post a **private** message
|
50
|
+
|
51
|
+
Have information that only a certain user should see??
|
52
|
+
|
53
|
+
Include a unique user id on the client and server
|
54
|
+
|
55
|
+
```javascript
|
56
|
+
hookly.setup('{{token}}', '{{uid}}')
|
57
|
+
hookly.on('#updates', function(options) {
|
58
|
+
// options == { model: 'Message', id: 6, text: 'Thanks for the PRIVATE info.' }
|
59
|
+
});
|
60
|
+
```
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
Hookly::Channel.new('#updates', uid: '{{uid}}').push(model: 'Message', id: 6, text: 'Thanks for the PRIVATE info.')
|
64
|
+
#=> #<Hookly::Message id: '44fjwq-djas' slug: '#updates', uid: '{{uid}}' data: { model: 'Message', id: 6, text: 'Thanks for the PRIVATE info.' }>
|
65
|
+
```
|
66
|
+
|
67
|
+
|
68
|
+
<!--- Not yet implemented
|
69
|
+
###Message buffering / caching
|
70
|
+
A channel can be setup to buffer messages and deliver them when a user comes online.
|
71
|
+
Simply create a buffered channel, messages will be cached for 60 minutes following their receipt
|
72
|
+
```ruby
|
73
|
+
Hookly::Channel.create(buffer: 3600)
|
74
|
+
```
|
75
|
+
-->
|
data/hookly.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'hookly/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'hookly'
|
7
|
+
s.version = Hookly::VERSION
|
8
|
+
s.authors = ['Brian Norton']
|
9
|
+
s.email = ['brian.nort@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/bnorton/hookly-rails'
|
11
|
+
s.summary = %q{hookly.js asset pipeline provider/wrapper. Ruby wrapper for the Hookly API}
|
12
|
+
s.license = 'MIT'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
|
19
|
+
s.add_development_dependency 'rspec', '~> 3.2'
|
20
|
+
s.add_development_dependency 'webmock', '~> 1.21'
|
21
|
+
|
22
|
+
s.add_dependency 'typhoeus', '~> 0.7'
|
23
|
+
end
|
data/lib/error.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'errors/response_error'
|
data/lib/hookly.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
##
|
2
|
+
# Dependencies
|
3
|
+
#
|
4
|
+
require 'json'
|
5
|
+
require 'typhoeus'
|
6
|
+
|
7
|
+
##
|
8
|
+
# Project files
|
9
|
+
#
|
10
|
+
require 'request'
|
11
|
+
require 'message'
|
12
|
+
require 'error'
|
13
|
+
|
14
|
+
module Hookly
|
15
|
+
%i(token secret url).each do |name|
|
16
|
+
define_singleton_method(name, &-> { instance_variable_get(:"@#{name}") })
|
17
|
+
define_singleton_method("#{name}=", &->(v) { instance_variable_set(:"@#{name}", v) })
|
18
|
+
end
|
19
|
+
|
20
|
+
module Rails
|
21
|
+
if defined?(::Rails)
|
22
|
+
class Rails::Engine < ::Rails::Engine
|
23
|
+
# this will enable the asset pipeline
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'hookly/version'
|
30
|
+
|
31
|
+
Hookly.url = 'https://hookly.herokuapp.com'
|
data/lib/message.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Hookly
|
2
|
+
class Message
|
3
|
+
attr_reader :id, :slug, :uid, :body
|
4
|
+
|
5
|
+
def self.create(slug, *args)
|
6
|
+
body = args.pop
|
7
|
+
|
8
|
+
new(Request.run(:post, :messages, { slug: slug, :uid => args.last }, body))
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
@id = options['id']
|
13
|
+
@slug = options['slug']
|
14
|
+
@uid = options['uid']
|
15
|
+
@body = options['body']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/request.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Hookly
|
2
|
+
class Request
|
3
|
+
def self.run(name, path, options, body={})
|
4
|
+
request_options = {
|
5
|
+
method: name,
|
6
|
+
accept_encoding: :gzip,
|
7
|
+
headers: { 'Content-Type' => 'application/json', 'User-Agent' => user_agent }
|
8
|
+
}
|
9
|
+
|
10
|
+
request_options[body_param(name)] = processed_body(name, options.merge( secret: Hookly.secret, :body => body ))
|
11
|
+
|
12
|
+
response = Typhoeus::Request.new("#{Hookly.url}/#{path}", request_options).run
|
13
|
+
|
14
|
+
if response.success?
|
15
|
+
JSON.parse(response.response_body)['message']
|
16
|
+
else
|
17
|
+
raise ResponseError.new(response)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.user_agent
|
24
|
+
"hookly-rails [language(ruby), lib-version(#{Hookly::VERSION}), platform(#{RUBY_PLATFORM}), platform-version(#{RUBY_VERSION})]"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.body_param(name)
|
28
|
+
%w(get delete).include?(name.to_s) ? :params : :body
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.processed_body(name, options)
|
32
|
+
%w(get delete).include?(name.to_s) ? options : JSON.dump(options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hookly::Message do
|
4
|
+
describe '.create' do
|
5
|
+
let(:options) { { id: 5, text: 'Thanks for the information!' } }
|
6
|
+
let(:create) { described_class.create('#updates', options) }
|
7
|
+
|
8
|
+
let!(:response) { run_response(id: 1440, slug: '#updates', body: options) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(Hookly::Request).to receive(:run).and_return(response)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should post to messages' do
|
15
|
+
expect(Hookly::Request).to receive(:run).with(:post, :messages, anything, anything).and_return(response)
|
16
|
+
|
17
|
+
create
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should specify the slug' do
|
21
|
+
expect(Hookly::Request).to receive(:run).with(anything, anything, hash_including(slug: '#updates'), anything).and_return(response)
|
22
|
+
|
23
|
+
create
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should send the body' do
|
27
|
+
expect(Hookly::Request).to receive(:run).with(anything, anything, anything, id: 5, text: 'Thanks for the information!').and_return(response)
|
28
|
+
|
29
|
+
create
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return the message' do
|
33
|
+
expect(create).to be_a(Hookly::Message)
|
34
|
+
expect(create.id).to eq(1440)
|
35
|
+
expect(create.slug).to eq('#updates')
|
36
|
+
expect(create.uid).to eq(nil)
|
37
|
+
expect(create.body).to eq('id' => 5, 'text' => 'Thanks for the information!')
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'when given a user identifier' do
|
41
|
+
let(:create) { described_class.create('#updates', '5a17b-124f5', id: 5, text: 'Thanks for the information!' ) }
|
42
|
+
|
43
|
+
it 'should specify the uid' do
|
44
|
+
expect(Hookly::Request).to receive(:run).with(anything, anything, hash_including(uid: '5a17b-124f5'), anything).and_return(response)
|
45
|
+
|
46
|
+
create
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hookly::Request do
|
4
|
+
describe '.run' do
|
5
|
+
let(:method) { %w(get post patch delete).sample }
|
6
|
+
let(:options) { { option1: 1, option2: 2 } }
|
7
|
+
let(:body) { double('Hash[body]') }
|
8
|
+
let(:run) { described_class.run(method, 'pathname', options, body) }
|
9
|
+
|
10
|
+
let!(:request) { double(:request, :run => response) }
|
11
|
+
let!(:response) { double(:response, :success? => true, :response_body => { response: 'body' }.to_json, :response_code => 900) }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(Typhoeus::Request).to receive(:new).and_return(request)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should send the url + path' do
|
18
|
+
expect(Typhoeus::Request).to receive(:new).with('http://test.host:1234/pathname', anything).and_return(request)
|
19
|
+
|
20
|
+
run
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should send the method' do
|
24
|
+
expect(Typhoeus::Request).to receive(:new).with(anything, hash_including(method: method)).and_return(request)
|
25
|
+
|
26
|
+
run
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should be json' do
|
30
|
+
expect(Typhoeus::Request).to receive(:new).with(anything, hash_including(headers: { 'Content-Type' => 'application/json', 'User-Agent' => "hookly-rails [language(ruby), lib-version(#{Hookly::VERSION}), platform(#{RUBY_PLATFORM}), platform-version(#{RUBY_VERSION})]" })).and_return(request)
|
31
|
+
|
32
|
+
run
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should run the request' do
|
36
|
+
expect(request).to receive(:run)
|
37
|
+
|
38
|
+
run
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should return the response body' do
|
42
|
+
expect(run).to eq({ 'response' => 'body' })
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'with a body' do
|
46
|
+
let(:method) { %w(post patch).sample }
|
47
|
+
|
48
|
+
it 'should send the auth + options + body' do
|
49
|
+
expected_body = { option1: 1, option2: 2, secret: 'test-config-secret', body: body }.to_json
|
50
|
+
expect(Typhoeus::Request).to receive(:new).with(anything, hash_including(body: expected_body)).and_return(request)
|
51
|
+
|
52
|
+
run
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'with url parameters' do
|
57
|
+
let(:method) { %w(get delete).sample }
|
58
|
+
|
59
|
+
it 'should send the auth + options + body' do
|
60
|
+
expected_params = { secret: 'test-config-secret', option1: 1, option2: 2, body: body }
|
61
|
+
expect(Typhoeus::Request).to receive(:new).with(anything, hash_including(params: expected_params)).and_return(request)
|
62
|
+
|
63
|
+
run
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'without a body' do
|
67
|
+
let(:run) { described_class.run(method, 'pathname', options) }
|
68
|
+
|
69
|
+
it 'should send the auth + options' do
|
70
|
+
expected_params = { secret: 'test-config-secret', option1: 1, option2: 2, body: {} }
|
71
|
+
expect(Typhoeus::Request).to receive(:new).with(anything, hash_including(params: expected_params)).and_return(request)
|
72
|
+
|
73
|
+
run
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'on error' do
|
79
|
+
before do
|
80
|
+
allow(response).to receive(:success?).and_return(false)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should error' do
|
84
|
+
expect {
|
85
|
+
run
|
86
|
+
}.to raise_error(Hookly::ResponseError, /failed with code: `900`/)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
|
4
|
+
require 'hookly'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.expect_with :rspec do |expectations|
|
8
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
9
|
+
end
|
10
|
+
|
11
|
+
config.mock_with :rspec do |mocks|
|
12
|
+
mocks.syntax = :expect
|
13
|
+
mocks.verify_partial_doubles = true
|
14
|
+
end
|
15
|
+
|
16
|
+
config.run_all_when_everything_filtered = true
|
17
|
+
config.profile_examples = 10
|
18
|
+
config.order = :defined
|
19
|
+
config.warnings = true
|
20
|
+
|
21
|
+
config.default_formatter = 'doc' if config.files_to_run.one? # More verbose when focused on one file
|
22
|
+
end
|
23
|
+
|
24
|
+
WebMock.disable_net_connect!
|
25
|
+
|
26
|
+
Dir[File.dirname(__FILE__) + '/helpers/*.rb'].each do |file|
|
27
|
+
require file
|
28
|
+
end
|