elk 0.0.13 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.MD +38 -12
- data/lib/elk.rb +31 -75
- data/lib/elk/client.rb +77 -0
- data/lib/elk/error.rb +16 -0
- data/lib/elk/number.rb +42 -18
- data/lib/elk/sms.rb +49 -24
- data/lib/elk/util.rb +13 -3
- data/lib/elk/version.rb +1 -1
- data/spec/fixtures/sends_a_sms_to_multiple_recipients.txt +1 -1
- data/spec/fixtures/sends_a_sms_with_long_sender.txt +2 -2
- data/spec/integration/number_spec.rb +191 -0
- data/spec/integration/sms_spec.rb +241 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +9 -3
- data/spec/unit/client_spec.rb +69 -0
- data/spec/unit/elk_spec.rb +70 -0
- data/spec/unit/number_spec.rb +93 -0
- data/spec/unit/sms_spec.rb +66 -0
- data/spec/unit/utils_spec.rb +50 -0
- metadata +27 -35
- data/spec/elk/number_spec.rb +0 -117
- data/spec/elk/sms_spec.rb +0 -168
- data/spec/elk_spec.rb +0 -58
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3184044f09e7d64a7822c28eaa6e3de56978fc9
|
4
|
+
data.tar.gz: db5a2368596d235da746132e3dae9e03dd56adf2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0ced3eef3b56b1225c8c1d92acf8a8a6f428f5105aa2e4a33e666a37c0db058d0bb0fc4348eea2d28232d4960944563aa1bc028ce1bb4e51f8c0cb60bdb4620
|
7
|
+
data.tar.gz: eeeec30f922cc461df8d750621b6c34a62097286848e7477bbef10e490e5af5cedad303355984529971a008a0fb866e72488eaa550da379deec663c4c7ca38c7
|
data/README.MD
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# Elk - 46elks API-client
|
2
2
|
|
3
|
+
[![Build Status](https://travis-ci.org/jage/elk.svg?branch=master)](https://travis-ci.org/jage/elk)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/jage/elk/badges/gpa.svg)](https://codeclimate.com/github/jage/elk)
|
5
|
+
|
3
6
|
Ruby client for 46elks "Voice, SMS & MMS" service. http://46elks.com/
|
4
7
|
At the moment the API only supports sending SMS messages.
|
5
8
|
|
6
9
|
## Requirements
|
7
10
|
|
8
|
-
|
9
|
-
|
11
|
+
* Modern Ruby: >= 1.9
|
10
12
|
* API account at 46elks.com
|
11
13
|
|
12
14
|
## Install
|
@@ -21,7 +23,11 @@ The source for Elk is available on Github:
|
|
21
23
|
|
22
24
|
https://github.com/jage/elk
|
23
25
|
|
24
|
-
|
26
|
+
Elk uses rspec and webmock for testing, do a `bundle install` for all the development requirements.
|
27
|
+
|
28
|
+
Test specs with:
|
29
|
+
|
30
|
+
bundle exec rake spec
|
25
31
|
|
26
32
|
## Usage
|
27
33
|
|
@@ -32,12 +38,32 @@ elk can be used to allocate a phone numbers, manage the numbers and send/recieve
|
|
32
38
|
First thing when using elk is to set the authentication parameters
|
33
39
|
|
34
40
|
```Ruby
|
35
|
-
require
|
41
|
+
require "elk"
|
36
42
|
|
37
43
|
Elk.configure do |config|
|
38
|
-
config.username =
|
39
|
-
config.password =
|
44
|
+
config.username = "USERNAME"
|
45
|
+
config.password = "PASSWORD"
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
It is possible to avoid the singleton configuration:
|
50
|
+
|
51
|
+
```Ruby
|
52
|
+
require "elk"
|
53
|
+
|
54
|
+
client = Elk::Client.new
|
55
|
+
client.configure do |config|
|
56
|
+
config.username = "USERNAME"
|
57
|
+
config.password = "PASSWORD"
|
40
58
|
end
|
59
|
+
|
60
|
+
# Then pass client to the class methods
|
61
|
+
numbers = Elk::Number.all(client: client)
|
62
|
+
# => [#<Elk::Number ...>, #<Elk::Number ...>]
|
63
|
+
|
64
|
+
|
65
|
+
Elk::SMS.send(client: client, from: "MyService", to: "+46704508449", message: "Your order #171 has now been sent!")
|
66
|
+
# => #<Elk::SMS:0x0000010179d7e8 @client=... @from="MyService", @to="+46704508449", @message="Your order #171 has now been sent!", @message_id="sdc39a7926d37159b6985283e32f43251", @created_at=2011-07-17 16:21:13 +0200, @loaded_at=2011-07-17 16:21:13 +0200>
|
41
67
|
```
|
42
68
|
|
43
69
|
### Numbers
|
@@ -45,7 +71,7 @@ end
|
|
45
71
|
To be able to send and recieve messages, a number is needed. Several numbers can be allocated.
|
46
72
|
|
47
73
|
```Ruby
|
48
|
-
number = Elk::Number.allocate(:
|
74
|
+
number = Elk::Number.allocate(sms_url: "http://myservice.se/callback/newsms.php", country: "se")
|
49
75
|
# => #<Elk::Number:0x0000010282aa70 @country="se", @sms_url="http://myservice.se/callback/newsms.php", @status="yes", @number_id="n03e7db70cc06c1ff85e09a2b3f86dd62", @number="+46766861034", @capabilities=[:sms], @loaded_at=2011-07-17 15:23:55 +0200>
|
50
76
|
```
|
51
77
|
|
@@ -59,7 +85,7 @@ numbers = Elk::Number.all
|
|
59
85
|
Change number settings
|
60
86
|
|
61
87
|
```Ruby
|
62
|
-
number.sms_url =
|
88
|
+
number.sms_url = "http://myservice.se/callback/newsms.php"
|
63
89
|
number.save
|
64
90
|
# => true
|
65
91
|
```
|
@@ -79,7 +105,7 @@ number.status
|
|
79
105
|
Send SMS. Messages can be sent from one of the allocated numbers or an arbitrary alphanumeric string of at most 11 characters.
|
80
106
|
|
81
107
|
```Ruby
|
82
|
-
Elk::SMS.send(:
|
108
|
+
Elk::SMS.send(from: "MyService", to: "+46704508449", message: "Your order #171 has now been sent!")
|
83
109
|
# => #<Elk::SMS:0x0000010179d7e8 @from="MyService", @to="+46704508449", @message="Your order #171 has now been sent!", @message_id="sdc39a7926d37159b6985283e32f43251", @created_at=2011-07-17 16:21:13 +0200, @loaded_at=2011-07-17 16:21:13 +0200>
|
84
110
|
```
|
85
111
|
|
@@ -87,8 +113,8 @@ Receiving SMS does not require Elk, but should be of interest anyway.
|
|
87
113
|
Example with Sinatra:
|
88
114
|
|
89
115
|
```Ruby
|
90
|
-
post
|
91
|
-
if request.params[
|
116
|
+
post "/receive" do
|
117
|
+
if request.params["message"] == "Hello"
|
92
118
|
# Sends a return SMS with message "world!"
|
93
119
|
"world!"
|
94
120
|
end
|
@@ -104,4 +130,4 @@ Elk::SMS.all
|
|
104
130
|
|
105
131
|
## Copyright
|
106
132
|
|
107
|
-
Copyright (c) 2011 Johan Eckerström. See LICENSE for details.
|
133
|
+
Copyright (c) 2011 Johan Eckerström. See [MIT-LICENSE](MIT-LICENSE) for details.
|
data/lib/elk.rb
CHANGED
@@ -1,93 +1,49 @@
|
|
1
|
-
|
2
|
-
require 'multi_json'
|
3
|
-
require 'open-uri'
|
4
|
-
require 'rest_client'
|
5
|
-
require 'time'
|
1
|
+
require "forwardable"
|
6
2
|
|
7
3
|
# Base module
|
8
4
|
# Used for to configure username and password through Elk.configure
|
9
5
|
module Elk
|
10
|
-
# Base domain for 46elks API
|
11
|
-
BASE_DOMAIN = 'api.46elks.com'
|
12
|
-
# API version supported
|
13
|
-
API_VERSION = 'a1'
|
14
|
-
|
15
|
-
# When the authentication can't be done
|
16
|
-
class AuthError < RuntimeError; end
|
17
|
-
# Raised when the API server isn't working
|
18
|
-
class ServerError < RuntimeError; end
|
19
|
-
# Generic exception when 46elks API gives a response Elk can't parse
|
20
|
-
class BadResponse < RuntimeError; end
|
21
|
-
# Generic exception when Elk calls 46elk API the wrong way
|
22
|
-
class BadRequest < RuntimeError; end
|
23
|
-
# Raised when required paremeters are omitted
|
24
|
-
class MissingParameter < RuntimeError; end
|
25
|
-
|
26
6
|
class << self
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# Delegate methods to client
|
10
|
+
delegated_methods = [
|
11
|
+
:username,
|
12
|
+
:username=,
|
13
|
+
:password,
|
14
|
+
:password=,
|
15
|
+
:base_domain,
|
16
|
+
:base_url,
|
17
|
+
:get,
|
18
|
+
:post,
|
19
|
+
:execute,
|
20
|
+
]
|
21
|
+
|
22
|
+
delegated_methods.each do |method|
|
23
|
+
def_delegator :client, method
|
24
|
+
end
|
33
25
|
|
34
26
|
# Set up authentication credentials, has to be done before using Elk::Number and Elk::SMS
|
35
27
|
#
|
36
28
|
# Elk.configure do |config|
|
37
|
-
# config.username =
|
38
|
-
# config.password =
|
29
|
+
# config.username = "USERNAME"
|
30
|
+
# config.password = "PASSWORD"
|
39
31
|
# end
|
40
32
|
def configure
|
41
|
-
yield
|
42
|
-
end
|
43
|
-
|
44
|
-
# Base URL used for calling 46elks API
|
45
|
-
def base_url
|
46
|
-
if not username or not password
|
47
|
-
raise AuthError, "API username and password required"
|
48
|
-
end
|
49
|
-
|
50
|
-
"https://#{username}:#{password}@#{(base_domain || BASE_DOMAIN)}/#{API_VERSION}"
|
51
|
-
end
|
52
|
-
|
53
|
-
# Wrapper for Elk.execute(:get)
|
54
|
-
def get(path, parameters = {})
|
55
|
-
execute(:get, path, parameters)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Wrapper for Elk::execute(:post)
|
59
|
-
def post(path, parameters = {})
|
60
|
-
execute(:post, path, parameters)
|
61
|
-
end
|
62
|
-
|
63
|
-
# Wrapper around RestClient::RestClient.execute
|
64
|
-
#
|
65
|
-
# * Sets accept header to json
|
66
|
-
# * Handles some exceptions
|
67
|
-
#
|
68
|
-
def execute(method, path, parameters, headers={:accept => :json}, &block)
|
69
|
-
payload = {}.merge(parameters)
|
70
|
-
url = base_url + path
|
71
|
-
RestClient::Request.execute(:method => method, :url => url, :payload => payload, :headers => headers, &block)
|
72
|
-
rescue RestClient::Unauthorized
|
73
|
-
raise AuthError, "Authentication failed"
|
74
|
-
rescue RestClient::InternalServerError
|
75
|
-
raise ServerError, "Server error"
|
76
|
-
rescue RestClient::Forbidden => e
|
77
|
-
raise BadRequest, e.http_body
|
33
|
+
yield client
|
78
34
|
end
|
79
35
|
|
80
|
-
#
|
81
|
-
def
|
82
|
-
|
83
|
-
rescue MultiJson::DecodeError
|
84
|
-
raise BadResponse, "Can't parse JSON"
|
36
|
+
# Not thread safe
|
37
|
+
def client
|
38
|
+
@client ||= Client.new
|
85
39
|
end
|
86
40
|
end
|
87
41
|
end
|
88
42
|
|
89
43
|
# Internal
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
44
|
+
require_relative "elk/util"
|
45
|
+
require_relative "elk/error"
|
46
|
+
require_relative "elk/version"
|
47
|
+
require_relative "elk/client"
|
48
|
+
require_relative "elk/number"
|
49
|
+
require_relative "elk/sms"
|
data/lib/elk/client.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require "rest_client"
|
2
|
+
|
3
|
+
module Elk
|
4
|
+
class Client
|
5
|
+
# Base domain for 46elks API
|
6
|
+
BASE_DOMAIN = "api.46elks.com"
|
7
|
+
# API version supported
|
8
|
+
API_VERSION = "a1"
|
9
|
+
|
10
|
+
# API Username from 46elks.com
|
11
|
+
attr_accessor :username
|
12
|
+
# API Password from 46elks.com
|
13
|
+
attr_accessor :password
|
14
|
+
# Used to overrid Elk::BASE_DOMAIN (in tests)
|
15
|
+
attr_accessor :base_domain
|
16
|
+
|
17
|
+
def initialize(parameters = {})
|
18
|
+
@username = parameters[:username]
|
19
|
+
@password = parameters[:password]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set authentication credentials
|
23
|
+
#
|
24
|
+
# client = Elk::Client.new
|
25
|
+
# client.configure do |config|
|
26
|
+
# config.username = "USERNAME"
|
27
|
+
# config.password = "PASSWORD"
|
28
|
+
# end
|
29
|
+
def configure
|
30
|
+
yield self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Base URL used for calling 46elks API
|
34
|
+
def base_url
|
35
|
+
unless username && password
|
36
|
+
raise AuthError, "API username and password required"
|
37
|
+
end
|
38
|
+
|
39
|
+
"https://#{username}:#{password}@#{(base_domain || BASE_DOMAIN)}/#{API_VERSION}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Wrapper for Elk.execute(:get)
|
43
|
+
def get(path, parameters = {})
|
44
|
+
execute(:get, path, parameters)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Wrapper for Elk::execute(:post)
|
48
|
+
def post(path, parameters = {})
|
49
|
+
execute(:post, path, parameters)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Wrapper around RestClient::RestClient.execute
|
53
|
+
#
|
54
|
+
# * Sets accept header to json
|
55
|
+
# * Handles some exceptions
|
56
|
+
#
|
57
|
+
def execute(method, path, parameters, headers = { accept: :json }, &block)
|
58
|
+
payload = {}.merge(parameters)
|
59
|
+
url = base_url + path
|
60
|
+
|
61
|
+
request_arguments = {
|
62
|
+
method: method,
|
63
|
+
url: url,
|
64
|
+
payload: payload,
|
65
|
+
headers: headers
|
66
|
+
}
|
67
|
+
|
68
|
+
RestClient::Request.execute(request_arguments, &block)
|
69
|
+
rescue RestClient::Unauthorized
|
70
|
+
raise AuthError, "Authentication failed"
|
71
|
+
rescue RestClient::InternalServerError
|
72
|
+
raise ServerError, "Server error"
|
73
|
+
rescue RestClient::Forbidden => e
|
74
|
+
raise BadRequest, e.http_body
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/elk/error.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Elk
|
2
|
+
# When the authentication can't be done
|
3
|
+
class AuthError < RuntimeError; end
|
4
|
+
|
5
|
+
# Raised when the API server isn't working
|
6
|
+
class ServerError < RuntimeError; end
|
7
|
+
|
8
|
+
# Generic exception when 46elks API gives a response Elk can't parse
|
9
|
+
class BadResponse < RuntimeError; end
|
10
|
+
|
11
|
+
# Generic exception when Elk calls 46elk API the wrong way
|
12
|
+
class BadRequest < RuntimeError; end
|
13
|
+
|
14
|
+
# Raised when required paremeters are omitted
|
15
|
+
class MissingParameter < RuntimeError; end
|
16
|
+
end
|
data/lib/elk/number.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require "time"
|
2
|
+
|
1
3
|
module Elk
|
2
4
|
# Allocate and manage numbers used for SMS/MMS/Voice
|
3
5
|
class Number
|
4
|
-
attr_reader :number_id, :number, :capabilities, :loaded_at #:nodoc:
|
6
|
+
attr_reader :number_id, :number, :capabilities, :loaded_at, :client #:nodoc:
|
5
7
|
attr_accessor :country, :sms_url, :voice_start_url #:nodoc:
|
6
8
|
|
7
9
|
def initialize(parameters) #:nodoc:
|
@@ -15,16 +17,17 @@ module Elk
|
|
15
17
|
@status = parameters[:active]
|
16
18
|
@number_id = parameters[:id]
|
17
19
|
@number = parameters[:number]
|
18
|
-
@capabilities = parameters[:capabilities].
|
20
|
+
@capabilities = Array(parameters[:capabilities]).map(&:to_sym)
|
19
21
|
@loaded_at = Time.now
|
22
|
+
@client = parameters.fetch(:client) { Elk.client }
|
20
23
|
end
|
21
24
|
|
22
25
|
# Status of a number, if it's :active or :deallocated
|
23
26
|
def status
|
24
27
|
case @status
|
25
|
-
when
|
28
|
+
when "yes"
|
26
29
|
:active
|
27
|
-
when
|
30
|
+
when "no"
|
28
31
|
:deallocated
|
29
32
|
else
|
30
33
|
nil
|
@@ -33,26 +36,30 @@ module Elk
|
|
33
36
|
|
34
37
|
# Reloads a number from the API server
|
35
38
|
def reload
|
36
|
-
response =
|
37
|
-
self.set_paramaters(Elk.parse_json(response.body))
|
39
|
+
response = @client.get("/Numbers/#{self.number_id}")
|
40
|
+
self.set_paramaters(Elk::Util.parse_json(response.body))
|
38
41
|
response.code == 200
|
39
42
|
end
|
40
43
|
|
41
44
|
# Updates or allocates a number
|
42
45
|
def save
|
43
|
-
attributes = {
|
46
|
+
attributes = {
|
47
|
+
sms_url: self.sms_url,
|
48
|
+
voice_start: self.voice_start_url
|
49
|
+
}
|
50
|
+
|
44
51
|
# If new URL, send country, otherwise not
|
45
|
-
|
52
|
+
unless self.number_id
|
46
53
|
attributes[:country] = self.country
|
47
54
|
end
|
48
|
-
response =
|
55
|
+
response = @client.post("/Numbers/#{self.number_id}", attributes)
|
49
56
|
response.code == 200
|
50
57
|
end
|
51
58
|
|
52
59
|
# Deallocates a number, once allocated, a number cannot be used again, ever!
|
53
60
|
def deallocate!
|
54
|
-
response =
|
55
|
-
self.set_paramaters(Elk.parse_json(response.body))
|
61
|
+
response = @client.post("/Numbers/#{self.number_id}", { active: "no" })
|
62
|
+
self.set_paramaters(Elk::Util.parse_json(response.body))
|
56
63
|
response.code == 200
|
57
64
|
end
|
58
65
|
|
@@ -62,19 +69,36 @@ module Elk
|
|
62
69
|
# Allocates a phone number
|
63
70
|
#
|
64
71
|
# * Required parameters: :country
|
65
|
-
# * Optional parameters: :sms_url, :voice_start_url
|
72
|
+
# * Optional parameters: :sms_url, :voice_start_url, :client
|
66
73
|
def allocate(parameters)
|
67
74
|
verify_parameters(parameters, [:country])
|
68
|
-
|
69
|
-
|
75
|
+
|
76
|
+
client = parameters.fetch(:client) { Elk.client }
|
77
|
+
|
78
|
+
allowed_arguments = [:country, :sms_url, :voice_start_url]
|
79
|
+
arguments = parameters.dup.select do |key, _|
|
80
|
+
allowed_arguments.include?(key)
|
81
|
+
end
|
82
|
+
|
83
|
+
response = client.post('/Numbers', arguments)
|
84
|
+
self.new(Elk::Util.parse_json(response.body))
|
70
85
|
end
|
71
86
|
|
72
87
|
# Returns all Elk::Numbers, regardless of status (allocated/deallocated)
|
73
|
-
|
74
|
-
|
88
|
+
#
|
89
|
+
# Optional parameters
|
90
|
+
#
|
91
|
+
# * :client - Elk::Client instance
|
92
|
+
#
|
93
|
+
def all(parameters = {})
|
94
|
+
client = parameters.fetch(:client) { Elk.client }
|
95
|
+
|
96
|
+
response = client.get('/Numbers')
|
75
97
|
|
76
|
-
Elk.parse_json(response.body)
|
77
|
-
|
98
|
+
numbers = Elk::Util.parse_json(response.body).fetch(:data)
|
99
|
+
numbers.map do |number|
|
100
|
+
number[:client] = client
|
101
|
+
self.new(number)
|
78
102
|
end
|
79
103
|
end
|
80
104
|
end
|