switchvox 0.1.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.
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2010-04-22
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,16 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/switchvox.rb
6
+ lib/switchvox/array.rb
7
+ lib/switchvox/base.rb
8
+ lib/switchvox/hash.rb
9
+ lib/switchvox/net_http_digest_auth.rb
10
+ lib/switchvox/object.rb
11
+ script/console
12
+ script/destroy
13
+ script/generate
14
+ test/test_helper.rb
15
+ test/test_json_to_obj.rb
16
+ test/test_switchvox.rb
@@ -0,0 +1,70 @@
1
+ = Switchvox
2
+
3
+ * http://github.com/chicks/switchvox
4
+
5
+ == SUMMARY:
6
+
7
+ Ruby Gem for interacting with Digium's Switchvox PBX via JSON
8
+
9
+ == DESCRIPTION:
10
+
11
+ Ruby Gem for interacting with Digium's Switchvox PBX via JSON.
12
+
13
+ There wasn't a gem out there, so I wrote one.
14
+
15
+ == FEATURES/PROBLEMS:
16
+
17
+ * The response hash gets converted into a Ruby object, via Hash#to_obj method.
18
+
19
+ == SYNOPSIS:
20
+
21
+ require 'switchvox'
22
+ switchvox = Switchvox::Base.new("pbx.foo.com", 'user', 'password', {:debug => false})
23
+
24
+ # Pull the software version of Switchvox
25
+ switchvox.request("switchvox.info.getSoftwareVersion")
26
+
27
+ # Pull a call report
28
+ call_log = switchvox.request("switchvox.callLogs.search", {
29
+ "start_date" => "2010-01-01 00:00:00",
30
+ "end_date" => "2010-01-02 23:59:59",
31
+ "account_ids" => [1001],
32
+ })
33
+ calls = call_log.calls.call
34
+ calls.each do |call|
35
+ c = call.to_obj
36
+ puts calls.index(call).to_s + ": " + c.start_time + ": " + c.from + " " + c.to
37
+ end
38
+
39
+ == REQUIREMENTS:
40
+
41
+ * json gem
42
+
43
+ == INSTALL:
44
+
45
+ * sudo gem install switchvox
46
+
47
+ == LICENSE:
48
+
49
+ (The MIT License)
50
+
51
+ Copyright (c) 2010 Carl Hicks
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining
54
+ a copy of this software and associated documentation files (the
55
+ 'Software'), to deal in the Software without restriction, including
56
+ without limitation the rights to use, copy, modify, merge, publish,
57
+ distribute, sublicense, and/or sell copies of the Software, and to
58
+ permit persons to whom the Software is furnished to do so, subject to
59
+ the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be
62
+ included in all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
65
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
67
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
68
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
69
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
70
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/switchvox'
6
+
7
+ Hoe.plugin :newgem
8
+ Hoe.plugin :git
9
+ # Hoe.plugin :website
10
+ # Hoe.plugin :cucumberfeatures
11
+
12
+ # Generate all the Rake tasks
13
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
14
+ $hoe = Hoe.spec 'switchvox' do
15
+ self.developer 'Carl Hicks', 'carl.hicks@gmail.com'
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ self.extra_deps = [['json']]
18
+
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # remove_task :default
26
+ # task :default => [:spec, :features]
@@ -0,0 +1,12 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'switchvox/hash'
5
+ require 'switchvox/array'
6
+ require 'switchvox/object'
7
+ require 'switchvox/net_http_digest_auth'
8
+ require 'switchvox/base'
9
+
10
+ module Switchvox
11
+ VERSION = '0.1.0'
12
+ end
@@ -0,0 +1,14 @@
1
+ # A fancy way of iterating over an array and converting hashes to objects
2
+ class Array
3
+ def to_obj
4
+ # Make a deep copy of the array
5
+ a = Marshal.load(Marshal.dump(self))
6
+ a.each do |i|
7
+ case i.class.to_s
8
+ when "Hash" then i = i.to_obj
9
+ when "Array" then i = i.to_obj
10
+ end
11
+ end
12
+ a
13
+ end
14
+ end
@@ -0,0 +1,145 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ module Switchvox
4
+
5
+ require 'uri'
6
+ require 'net/https'
7
+ require 'openssl'
8
+ require 'digest/md5'
9
+ require 'rubygems'
10
+ require 'json'
11
+
12
+ # Raised when credentials are incorrect
13
+ class LoginError < RuntimeError
14
+ end
15
+
16
+ # Raised when the response.body is empty
17
+ class EmptyResponse < RuntimeError
18
+ end
19
+
20
+ # Raised when a response is returned that we don't know how to handle
21
+ class UnhandledResponse < RuntimeError
22
+ end
23
+
24
+ # The primary class used to interact with Switchvox.
25
+ class Base
26
+
27
+ URL = "/json"
28
+ attr :host, true
29
+ attr :url, true
30
+ attr :user, false
31
+ attr :pass, false
32
+ attr :connection, true
33
+ attr :session, true
34
+ attr :debug, true
35
+ attr :auth_header, true
36
+
37
+ def initialize(host, user, pass, options={})
38
+ {:debug => false}.merge! options
39
+ @debug = options[:debug]
40
+
41
+ @host = host
42
+ @user = user
43
+ @pass = pass
44
+ @url = URI.parse("https://" + @host + URL)
45
+ @ssl = false
46
+ @ssl = true if @url.scheme == "https"
47
+
48
+ @connection = false
49
+ @auth_header = false
50
+ login!
51
+ raise LoginError, "Invalid Username or Password" unless logged_in?
52
+ end
53
+
54
+ # A standard REST call to get a list of entries
55
+ def request(method, parameters={})
56
+ login! unless logged_in?
57
+ json = wrap_json(method, parameters)
58
+
59
+ # Send the request
60
+ header = {'Content-Type' => "text/json"}
61
+ request = Net::HTTP::Post.new(@url.path, header)
62
+ request.digest_auth(@user, @pass, @auth_header)
63
+ request.body = json
64
+ response = @connection.request(request)
65
+
66
+ if @debug
67
+ puts "#{method}: Request"
68
+ puts json
69
+ puts "\n"
70
+ end
71
+
72
+ case response
73
+ when Net::HTTPOK
74
+ raise EmptyResponse unless response.body
75
+ response_json = JSON.parse response.body
76
+ if @debug
77
+ puts "#{method}: Response:"
78
+ pp response_json
79
+ puts "\n\n"
80
+ end
81
+ response_obj = response_json["response"]["result"].to_obj
82
+ return response_obj
83
+ when Net::HTTPUnauthorized
84
+ login!
85
+ request(method, parameters)
86
+ when Net::HTTPForbidden
87
+ raise LoginError, "Invalid Username or Password"
88
+ else raise UnhandledResponse, "Can't handle response #{response}"
89
+ end
90
+ end
91
+
92
+ protected
93
+
94
+ # Check to see if we are logged in
95
+ def logged_in?
96
+ return false unless @auth_header
97
+ true
98
+ end
99
+
100
+ # Attempt HTTP Digest Authentication with Switchvox
101
+ def login!
102
+ connect! unless connected?
103
+ @auth_header = @connection.head(@url.path)
104
+ end
105
+
106
+ # Check to see if we have an HTTP/S Connection
107
+ def connected?
108
+ return false unless @connection
109
+ return false unless @connection.started?
110
+ true
111
+ end
112
+
113
+ # Connect to the remote Switchvox system. If SSL is enabled, ignore certificate checking.
114
+ def connect!
115
+ @connection = Net::HTTP.new(@url.host, @url.port)
116
+ if @ssl
117
+ @connection.use_ssl = true
118
+ @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
119
+ end
120
+ if @debug
121
+ #@connection.set_debug_output $stderr
122
+ end
123
+ @connection.start
124
+ end
125
+
126
+ # Wrap a JSON method and parameters in a Switchvox compatible request body.
127
+ def wrap_json(method, parameters={})
128
+ json = <<-"EOF"
129
+ {
130
+ \"request\": {
131
+ \"method\": \"#{method}\",
132
+ \"parameters\": #{parameters.to_json}
133
+ }
134
+ }
135
+ EOF
136
+ json.gsub!(/^\s{8}/,'')
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+
143
+
144
+
145
+
@@ -0,0 +1,17 @@
1
+ # A fancy way of iterating over a hash and converting hashes to objects
2
+ class Hash
3
+ def to_obj
4
+ o = Object.new
5
+ self.each do |k,v|
6
+ # If we're looking at a hash or array, we need to look through them and convert any hashes to objects as well
7
+ case v.class.to_s
8
+ when "Hash" then v = v.to_obj
9
+ when "Array" then v = v.to_obj
10
+ end
11
+ o.instance_variable_set("@#{k}", v) ## create and initialize an instance variable for this key/value pair
12
+ o.class.send(:define_method, k, proc{o.instance_variable_get("@#{k}")}) ## create the getter that returns the instance variable
13
+ o.class.send(:define_method, "#{k}=", proc{|v| o.instance_variable_set("@#{k}", v)}) ## create the setter that sets the instance variable
14
+ end
15
+ o
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ module Net
2
+ module HTTPHeader
3
+ require 'digest/md5'
4
+ require 'net/http'
5
+
6
+ @@nonce_count = -1
7
+ CNONCE = Digest::MD5.new.update("%x" % (Time.now.to_i + rand(65535))).hexdigest
8
+ def digest_auth(user, password, response)
9
+ # based on http://segment7.net/projects/ruby/snippets/digest_auth.rb
10
+ @@nonce_count += 1
11
+
12
+ response['www-authenticate'] =~ /^(\w+) (.*)/
13
+
14
+ params = {}
15
+ $2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
16
+
17
+ a_1 = "#{user}:#{params['realm']}:#{password}"
18
+ a_2 = "#{@method}:#{@path}"
19
+ request_digest = ''
20
+ request_digest << Digest::MD5.new.update(a_1).hexdigest
21
+ request_digest << ':' << params['nonce']
22
+ request_digest << ':' << Digest::MD5.new.update(a_2).hexdigest
23
+
24
+ header = []
25
+ header << "Digest username=\"#{user}\""
26
+ header << "realm=\"#{params['realm']}\""
27
+ header << "uri=\"#{@path}\""
28
+ header << "nonce=\"#{params['nonce']}\""
29
+ header << "response=\"#{Digest::MD5.new.update(request_digest).hexdigest}\""
30
+
31
+ @header['Authorization'] = header
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ class Object
2
+ def to_obj
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/switchvox.rb'}"
9
+ puts "Loading switchvox gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,4 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require 'pp'
4
+ require File.dirname(__FILE__) + '/../lib/switchvox'
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestJson2Object < Test::Unit::TestCase
4
+ def test_object
5
+ json = {"string" => "value"}.to_json
6
+ obj = JSON.parse(json).to_obj
7
+ assert_equal("value", obj.string)
8
+ end
9
+
10
+ def test_nested_object
11
+ json = {"dogs" => {"retriever" => "sparky", "basset" => "jennie", "pinscher" => "carver"}}.to_json
12
+ obj = JSON.parse(json).to_obj
13
+ assert_equal("sparky", obj.dogs.retriever)
14
+ end
15
+
16
+ def test_array_of_objects
17
+ json = [{"retriever" => "sparky"}, {"basset" => "jennie"}, {"pinscher" => "carver"}].to_json
18
+ obj = JSON.parse(json).to_obj
19
+ assert_equal("sparky", obj[0].retriever)
20
+ end
21
+
22
+ def test_deep_nest_mixed
23
+ json = {"kennels" => [
24
+ {"dallas" => [
25
+ {"name" => "north"},
26
+ {"name" => "east"},
27
+ ]},
28
+ {"frisco" => [
29
+ {"name" => "south"},
30
+ {"name" => "west"}
31
+ ],
32
+ "company" => "Doggie Daze"
33
+ }
34
+ ]}.to_json
35
+ obj = JSON.parse(json).to_obj
36
+ assert_equal("west", obj.kennels[1].frisco[0].name)
37
+ end
38
+
39
+ def test_deep_nest_hash
40
+ json = {"kennels" => {
41
+ "kennel" => {
42
+ "dallas" => ["north", "south"],
43
+ "frisco" => ["east", "west"]}}
44
+ }.to_json
45
+ obj = JSON.parse(json).to_obj
46
+ pp obj
47
+ assert_equal("north", obj.kennels.kennel.dallas[0])
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestSwitchvox < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def test_truth
9
+ assert true
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: switchvox
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Carl Hicks
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-22 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: rubyforge
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 2
41
+ - 0
42
+ - 4
43
+ version: 2.0.4
44
+ type: :development
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: hoe
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 2
55
+ - 6
56
+ - 0
57
+ version: 2.6.0
58
+ type: :development
59
+ version_requirements: *id003
60
+ description: |-
61
+ Ruby Gem for interacting with Digium's Switchvox PBX via JSON.
62
+
63
+ There wasn't a gem out there, so I wrote one.
64
+ email:
65
+ - carl.hicks@gmail.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files:
71
+ - History.txt
72
+ - Manifest.txt
73
+ files:
74
+ - History.txt
75
+ - Manifest.txt
76
+ - README.rdoc
77
+ - Rakefile
78
+ - lib/switchvox.rb
79
+ - lib/switchvox/array.rb
80
+ - lib/switchvox/base.rb
81
+ - lib/switchvox/hash.rb
82
+ - lib/switchvox/net_http_digest_auth.rb
83
+ - lib/switchvox/object.rb
84
+ - script/console
85
+ - script/destroy
86
+ - script/generate
87
+ - test/test_helper.rb
88
+ - test/test_json_to_obj.rb
89
+ - test/test_switchvox.rb
90
+ has_rdoc: true
91
+ homepage: http://github.com/chicks/switchvox
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options:
96
+ - --main
97
+ - README.rdoc
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ requirements: []
115
+
116
+ rubyforge_project: switchvox
117
+ rubygems_version: 1.3.6
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Ruby Gem for interacting with Digium's Switchvox PBX via JSON
121
+ test_files:
122
+ - test/test_helper.rb
123
+ - test/test_json_to_obj.rb
124
+ - test/test_switchvox.rb