routengn 0.2.9

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.
data/README.rdoc ADDED
@@ -0,0 +1,61 @@
1
+ = Introduction
2
+
3
+ This is a Ruby API Wrapper for the RouteNGN SIP routing platform. For more information about
4
+ RouteNGN, see http://www.routengn.com.
5
+
6
+ * Note: This is not the RouteNGN API itself. This a wrapper designed to make it easier to integrate with the RouteNGN API.
7
+
8
+ = Installation
9
+
10
+ Install the "routengn" gem by typing
11
+
12
+ $ sudo gem sources -a http://gems.github.com
13
+ $ sudo gem install klarrimore-routengn
14
+
15
+ The first line tells RubyGems to add the GitHub gem repository. You only need
16
+ to run this command once.
17
+
18
+ = Examples
19
+
20
+ Using irb:
21
+
22
+ klarrimore@calculatorwristwatch:~$ irb
23
+ irb(main):001:0> require 'rubygems'
24
+ => true
25
+ irb(main):002:0> require 'routengn'
26
+ => true
27
+ irb(main):003:0> RouteNGN.connect! "http://routengn.net", "qcuL3YAw9ZXdmSdqprBw", "LDI2rgxIhJlNu5FBXh0UvtxJRtpr1OgnCIQ8Mpk"
28
+ => #<RouteNGN::Connection:0x7ffaadc3f578 @access_token=#<OAuth::AccessToken:0x....
29
+ irb(main):004:0> Carrier.all
30
+ => [#<Carrier:0x7f1c6e36ce28 @id=152, @saved=true, @name="test">]
31
+ irb(main):005:0> Carrier.first.groups
32
+ => [#<Group:0x7f1c6e335c70 @active_end_day=nil, @id=103, @add_type=nil, @carrier_id=152, @strip_digits=nil,
33
+ @active_begin_day=nil, @add_field=nil, @saved=true, @active_end_time=nil, @margin=0, @trunkgroup=nil,
34
+ @add_digits=nil, @active_begin_time=nil, @direction="in", @name="test2", @remove_rn=nil, @prefix=nil,
35
+ @rn_prefix=nil>, #<Group:0x7f1c6e335c48 @active_end_day=nil, @id=102, @add_type=nil, @carrier_id=152,
36
+ @strip_digits=nil, @active_begin_day=nil, @add_field=nil, @saved=true, @active_end_time=nil, @margin=0, @trunkgroup=nil,
37
+ @add_digits=nil, @active_begin_time=nil, @direction="out", @name="test", @remove_rn=nil, @prefix=nil, @rn_prefix=nil>]
38
+ irb(main):006:0> Carrier.first.groups.first.endpoints
39
+ => [#<Endpoint:0x7f1c6e2f21c8 @id=39, @saved=true, @ip="65.200.189.134">]
40
+
41
+ = Credits
42
+
43
+ klarrimore@gentel.net
44
+ qhoxie@gentel.net
45
+
46
+ = License
47
+
48
+ Copyright 2008-2009 GT3 HOLDINGS, LLC
49
+
50
+ Licensed under the Apache License, Version 2.0 (the "License");
51
+ you may not use this file except in compliance with the License.
52
+ You may obtain a copy of the License at
53
+
54
+ http://www.apache.org/licenses/LICENSE-2.0
55
+
56
+ Unless required by applicable law or agreed to in writing, software
57
+ distributed under the License is distributed on an "AS IS" BASIS,
58
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
59
+ See the License for the specific language governing permissions and
60
+ limitations under the License.
61
+
data/bin/ngncmd ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'thor'
5
+ require File.dirname(__FILE__) + '/../lib/routengn'
6
+
7
+ class NGNCommand < Thor
8
+ map "--configure" => :configure,
9
+ "--upload-regions" => :region_upload
10
+
11
+ desc "configure", "Set up your RouteNGN API config (.ngnconf)"
12
+ def configure
13
+ user = ENV['USER'] ? ENV['USER'] : `who am i`
14
+ say "Configuring ngncmd for #{user}...", :yellow
15
+ site = ask "RouteNGN Site (ex. http://routengn.net):", :magenta
16
+ key = ask "API Key:", :magenta
17
+ secret = ask "API Secret:", :magenta
18
+ say "Trying to connect... ", :yellow
19
+ begin
20
+ if RouteNGN.connect! site, key, secret
21
+ say "Success", :green
22
+ say "Writing config file... ", :yellow
23
+ RouteNGN.write_config
24
+ return say("Success", :green)
25
+ end
26
+ rescue Exception
27
+ # noop
28
+ end
29
+ say "Failed", :red
30
+ say "\nPlease check your site and credentials and try again.", :red
31
+ exit 1
32
+ end
33
+
34
+ desc "upload regions", "Upload a regions file"
35
+ def region_upload
36
+ file = ask "Path to file:", :magenta
37
+ unless File.readable? file
38
+ say "Error: file is not readable"
39
+ exit 1
40
+ end
41
+ connect
42
+ say "Uploading file... "
43
+ Region.upload(file)
44
+ say "Success"
45
+ end
46
+
47
+
48
+ no_tasks do
49
+ def connect
50
+ say "Establishing connection... "
51
+ RouteNGN.connect!
52
+ say "Success"
53
+ rescue Exception
54
+ say "Failed"
55
+ exit 1
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ NGNCommand.start
62
+
Binary file
data/examples/cli.rb ADDED
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
4
+
5
+ require 'rubygems'
6
+ require 'json'
7
+ gem 'oauth'
8
+ require 'oauth/consumer'
9
+ require 'http/multipart'
10
+
11
+ @consumer=OAuth::Consumer.new "qcuL3sYAw9ZXdmSdqprBw", #API key
12
+ "LDI2rgxnIhJlNu5FBXh0UvtxJRtpr1OgnCIQ8Mpk", #API secret
13
+ { :signature_method => 'HMAC-SHA1',
14
+ :site=>"http://127.0.0.1:3000" #EX: http://routengn.net
15
+ }
16
+
17
+ @request_token = @consumer.get_request_token
18
+
19
+ @consumer.request(:get, @request_token.authorize_url)
20
+
21
+ @access_token = @request_token.get_access_token
22
+ @access_token.consumer.http.read_timeout = 5000
23
+
24
+ def main_menu
25
+ puts "\nMain Menu:\n1. manage carriers\n2. manage regions\n3. upload regions or rates\n4. manage route tables\n"
26
+ str = gets.chomp
27
+ case str
28
+ when '1'
29
+ carrier_menu
30
+ when '2'
31
+ region_menu
32
+ when '3'
33
+ upload_menu
34
+ when '4'
35
+ route_table_menu
36
+ else
37
+ puts 'wtf'
38
+ end
39
+ end
40
+
41
+ def carrier_menu
42
+ puts "\nCarrier Menu:\n1. list carriers\n2. list groups\n3. list end points\n4. add carriers\n5. add groups\n6. add end points\n7. delete carriers\n8. delete groups\n9. delete end points\n"
43
+ str = gets.chomp
44
+ case str
45
+ when '1' #list carriers
46
+ puts "\n" + JSON.parse(@access_token.get('/carrier/carrier').body).inspect
47
+ when '2' #list groups
48
+ print "carrier id: "
49
+ carrier_id = gets.chomp
50
+ puts "\n" + JSON.parse(@access_token.get("/carrier/group/#{carrier_id}").body).inspect
51
+ when '3' #list end points
52
+ print "group id: "
53
+ group_id = gets.chomp
54
+ puts "\n" + JSON.parse(@access_token.get("/carrier/endpoint/#{group_id}").body).inspect
55
+ when '4' #add carriers
56
+ print "carrier name: "
57
+ carrier_name = gets.chomp
58
+ puts "\n" + JSON.parse(@access_token.post("/carrier/carrier/#{carrier_name}").body).inspect
59
+ when '5' #add groups
60
+ print "carrier id: "
61
+ carrier_id = gets.chomp
62
+ print "group name: "
63
+ group_name = gets.chomp
64
+ puts "\n" + JSON.parse(@access_token.post("/carrier/group/#{group_name}/#{carrier_id}").body).inspect
65
+ when '6' #add end points
66
+ print "group id: "
67
+ group_id = gets.chomp
68
+ print "end point name: "
69
+ endpoint_name = gets.chomp
70
+ puts "\n" + JSON.parse(@access_token.post("/carrier/endpoint/#{endpoint_name}/#{group_id}").body).inspect
71
+ when '7' #delete carriers
72
+ print "carrier id: "
73
+ carrier_id = gets.chomp
74
+ puts "\n" + JSON.parse(@access_token.delete("/carrier/carrier/#{carrier_id}").body).inspect
75
+ when '8' #delete groups
76
+ print "group id: "
77
+ group_id = gets.chomp
78
+ puts "\n" + JSON.parse(@access_token.delete("/carrier/group/#{group_id}").body).inspect
79
+ when '9' #delete end points
80
+ print "end point id: "
81
+ endpoint_id = gets.chomp
82
+ puts "\n" + JSON.parse(@access_token.delete("/carrier/endpoint/#{endpoint_id}").body).inspect
83
+ when 'back'
84
+ return
85
+ end
86
+ end
87
+
88
+ def region_menu
89
+ puts "\nRegion Menu:\n1. list regions\n2. list locales\n3. list dial codes\n4. add regions\n5. add locales\n6. add dial codes\n7. delete regions\n8. delete locales\n9. delete dial codes\n"
90
+ str = gets.chomp
91
+ case str
92
+ when '1' #list regions
93
+ puts "\n" + JSON.parse(@access_token.get('/region/region').body).inspect
94
+ when '2' #list locales
95
+ print "region id: "
96
+ region_id = gets.chomp
97
+ puts "\n" + JSON.parse(@access_token.get("/region/locale/#{region_id}").body).inspect
98
+ when '3' #list dial codes
99
+ print "locale id: "
100
+ locale_id = gets.chomp
101
+ puts "\n" + JSON.parse(@access_token.get("/region/dialcode/#{locale_id}").body).inspect
102
+ when '4' #add regions
103
+ print "region name: "
104
+ region_name = gets.chomp
105
+ puts "\n" + JSON.parse(@access_token.post("/region/region/#{region_name}").body).inspect
106
+ when '5' #add locales
107
+ print "region id: "
108
+ region_id = gets.chomp
109
+ print "locale name: "
110
+ locale_name = gets.chomp
111
+ puts "\n" + JSON.parse(@access_token.post("/region/locale/#{locale_name}/#{region_id}").body).inspect
112
+ when '6' #add dial codes
113
+ print "locale id: "
114
+ locale_id = gets.chomp
115
+ print "dialcode: "
116
+ dialcode = gets.chomp
117
+ puts "\n" + JSON.parse(@access_token.post("/region/dialcode/#{dialcode}/#{locale_id}").body).inspect
118
+ when '7' #delete regions
119
+ print "region id: "
120
+ region_id = gets.chomp
121
+ puts "\n" + JSON.parse(@access_token.delete("/region/region/#{region_id}").body).inspect
122
+ when '8' #delete locales
123
+ print "locale id: "
124
+ locale_id = gets.chomp
125
+ puts "\n" + JSON.parse(@access_token.delete("/region/locale/#{locale_id}").body).inspect
126
+ when '9' #delete dial codes
127
+ print "dialcode id: "
128
+ dialcode_id = gets.chomp
129
+ puts "\n" + JSON.parse(@access_token.delete("/region/dialcode/#{dialcode_id}").body).inspect
130
+ when 'back'
131
+ return
132
+ end
133
+ end
134
+
135
+ def upload_menu
136
+ puts "\nUpload Menu:\n1. upload regions\n2. upload rates\n"
137
+ str = gets.chomp
138
+ case str
139
+ when '1'
140
+ print "path: "
141
+ path = gets.chomp
142
+ data, headers = Multipart::Post.prepare_query("title" => 'title', "uploaded_data" => File.new(path))
143
+ puts @access_token.put('/upload/regions', data, headers)
144
+ when '2'
145
+ print "path: "
146
+ path = gets.chomp
147
+ print "rate type: "
148
+ rate_type = gets.chomp
149
+ print "groups: "
150
+ groups = gets.chomp
151
+ data, headers = Multipart::Post.prepare_query("title" => 'title', "uploaded_data" => File.new(path))
152
+ puts @access_token.put("/upload/rates/#{rate_type}/#{groups}", data, headers)
153
+ when 'back'
154
+ return
155
+ end
156
+ end
157
+
158
+ def route_table_menu
159
+ puts "\nRoute Table Menu:\n1. list instances\n2. list routes\n3. create instance\n4. generate lcr\n5. search\n"
160
+ str = gets.chomp
161
+ case str
162
+ when '1'
163
+ puts @access_token.get('/route/instances').body
164
+ when '2'
165
+
166
+ puts @access_token.get('/route/routes/13').body
167
+ when '3'
168
+ print "table name: "
169
+ table_name = gets.chomp
170
+ print "table type: "
171
+ table_type = gets.chomp
172
+ puts @access_token.post("/route/table/#{table_name}/#{table_type}").body
173
+ when '4'
174
+ print "table type: "
175
+ table_type = gets.chomp
176
+ print "instance id: "
177
+ instance_id = gets.chomp
178
+ print "groups: "
179
+ groups = gets.chomp
180
+ puts @access_token.put("/route/update/lcr/#{table_type}/#{instance_id}/#{groups}").body
181
+ when '5'
182
+ print "search type: "
183
+ search_type = gets.chomp
184
+ print "instance id: "
185
+ instance_id = gets.chomp
186
+ print "table type: "
187
+ table_type = gets.chomp
188
+ print "route type: "
189
+ route_type = gets.chomp
190
+ print "search: "
191
+ search = gets.chomp
192
+ puts @access_token.get("/route/search/#{search_type}/#{instance_id}/#{table_type}/#{route_type}/#{search}").body
193
+ when 'back'
194
+ return
195
+ end
196
+ end
197
+
198
+ while true
199
+ main_menu
200
+ end
201
+
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'routengn'
5
+
6
+ RouteNGN.connect! "http://beta.routengn.net", "qcuL3sYAw9ZXdmSdqprBw", "LDI2rgxnIhJlNu5FBXh0UvtxJRtpr1OgnCIQ8Mpk"
7
+
8
+ Region.upload('my_regions.csv')
9
+
10
+ carrier = Carrier.new({'name' => 'api_test_carrier'})
11
+ carrier.save
12
+ puts carrier.inspect
13
+
14
+ group = Group.new({'name' => 'api_test_group', 'direction' => 'out', 'carrier_id' => carrier.id})
15
+ group.save
16
+ puts group.inspect
17
+
18
+ endpoint = Endpoint.new({'ip' => '3.3.3.3', 'epgroup_id' => group.id})
19
+ endpoint.save
20
+ puts endpoint.inspect
21
+
22
+ type_id = Type.first('name'=> 'lcr').id
23
+
24
+ instance = Instance.new('name' => 'api_test_table', 'type_id' => type_id)
25
+ instance.save
26
+ puts instance.inspect
27
+
28
+ Rate.upload('my_rates.csv', 'group' => group.id, 'type_id' => type_id)
29
+
30
+ Instance.first.generate(Group.all.collect{|x| x.id})
31
+
32
+ RouteNGN.apply!
@@ -0,0 +1,18 @@
1
+ HISTFILE = "~/.routengn.irb.hist"
2
+ MAXHISTSIZE = 1000
3
+
4
+ if defined? Readline::HISTORY
5
+ histfile = File.expand_path(HISTFILE)
6
+ if File.exists?(histfile)
7
+ lines = IO.readlines(histfile).collect { |line| line.chomp }
8
+ Readline::HISTORY.push(*lines)
9
+ end
10
+
11
+ Kernel.at_exit do
12
+ lines = Readline::HISTORY.to_a.reverse.uniq.reverse
13
+ lines = lines[-MAXHISTSIZE, MAXHISTSIZE] if lines.nitems > MAXHISTSIZE
14
+ File.open(histfile, File::WRONLY|File::CREAT|File::TRUNC) do |f|
15
+ lines.each { |line| f.puts line }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,64 @@
1
+ module RouteNGN
2
+ module HTTP
3
+ module Multipart
4
+ # From: http://deftcode.com/code/flickr_upload/multipartpost.rb
5
+ ## Helper class to prepare an HTTP POST request with a file upload
6
+ ## Mostly taken from
7
+ #http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/113774
8
+ ### WAS:
9
+ ## Anything that's broken and wrong probably the fault of Bill Stilwell
10
+ ##(bill@marginalia.org)
11
+ ### NOW:
12
+ ## Everything wrong is due to keith@oreilly.com
13
+
14
+ BOUNDARY = 'larrimores-rule0000'
15
+ HEADER = {"Content-type" => "multipart/form-data, boundary=" + BOUNDARY + " "}
16
+
17
+ class Param
18
+ attr_accessor :k, :v
19
+
20
+ def initialize( k, v )
21
+ @k = k
22
+ @v = v
23
+ end
24
+
25
+ def to_multipart
26
+ #return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"\r\n\r\n#{v}\r\n"
27
+ # Don't escape mine...
28
+ return "Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n#{v}\r\n"
29
+ end
30
+ end
31
+
32
+ class FileParam
33
+ attr_accessor :k, :filename, :content
34
+
35
+ def initialize( k, filename, content )
36
+ @k = k
37
+ @filename = filename
38
+ @content = content
39
+ end
40
+
41
+ def to_multipart
42
+ #return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"; filename=\"#{filename}\"\r\n" + "Content-Transfer-Encoding: binary\r\n" + "Content-Type: #{MIME::Types.type_for(@filename)}\r\n\r\n" + content + "\r\n "
43
+ # Don't escape mine
44
+ return "Content-Disposition: form-data; name=\"#{k}\"; filename=\"#{filename}\"\r\n" + "Content-Transfer-Encoding: binary\r\n" + "Content-Type: #{MIME::Types.type_for(@filename)}\r\n\r\n" + content + "\r\n"
45
+ end
46
+ end
47
+
48
+ def self.prepare_query(params)
49
+ fp = []
50
+ params.each {|k, v|
51
+ if v.respond_to?(:read)
52
+ fp.push(FileParam.new(k, v.path, v.read))
53
+ else
54
+ fp.push(Param.new(k, v))
55
+ end
56
+ }
57
+ query = fp.collect {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("") + "--" + BOUNDARY + "--"
58
+ return query, HEADER
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+
@@ -0,0 +1,159 @@
1
+ module RouteNGN
2
+ module Mapper
3
+ def self.included(model)
4
+ model.class_eval do
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+ end
8
+ end
9
+ end
10
+
11
+ #TODO perhaps we can add some of the goodies provided through relationships like '<<'
12
+ module InstanceMethods
13
+ attr_accessor :saved
14
+
15
+ def attributes
16
+ self.class.fields.inject({}) do |_, field|
17
+ _[field] = send field
18
+ _
19
+ end
20
+ end
21
+
22
+ def attr_params
23
+ self.attributes.inject({}) do |params, (k, v)|
24
+ params["#{self.class.name.downcase}[#{k}]"] = v
25
+ params
26
+ end
27
+ end
28
+
29
+ def new?
30
+ !(@saved ||= false)
31
+ end
32
+
33
+ def save
34
+ response = if new?
35
+ RouteNGN.post self.class.base_url, attr_params
36
+ else
37
+ RouteNGN.put self.class.base_url, attr_params
38
+ end
39
+
40
+ return false if !response.success?
41
+
42
+ @saved = true
43
+
44
+ # populates fields with data that was sent back
45
+ self.class.fields.each do |field|
46
+ element = response['data'].first
47
+ self.send :"#{field}=", element[field.to_s] if element[field.to_s]
48
+ end
49
+
50
+ true
51
+ end
52
+
53
+ def destroy
54
+ response = RouteNGN.delete self.class.base_url, :id => primary
55
+ response.success?
56
+ end
57
+ end # InstanceMethods
58
+
59
+ module ClassMethods
60
+ attr_reader :fields
61
+ attr_reader :primary_field
62
+
63
+ def new(opts = {})
64
+ instance = super() # don't want implicit args
65
+ @fields.each do |field|
66
+ field = field.to_s
67
+ next unless opts.has_key? field
68
+ instance.send :"#{field}=", opts[field]
69
+ end
70
+ raise PrimaryFieldException.new unless instance.respond_to? :primary
71
+ instance
72
+ end
73
+
74
+ def new_from_saved(opts = {})
75
+ instance = new opts
76
+ instance.saved = true
77
+ instance
78
+ end
79
+
80
+ def base_url
81
+ "/#{name.downcase.pluralize}"
82
+ end
83
+
84
+ def field(name, opts = {})
85
+ @fields ||= []
86
+ return if @fields.include? name
87
+ @fields << name
88
+ define_method(name) { instance_variable_get :"@#{name}" }
89
+ define_method(:"#{name}=") { |val| instance_variable_set :"@#{name}", val }
90
+ if opts[:primary]
91
+ raise MultiplePrimaryFieldsException.new if @primary_field
92
+ alias_method :primary, name
93
+ @primary_field = name
94
+ end
95
+ end
96
+
97
+ def belongs_to(klass, opts = {}) # This isn't really a klass (in the traditional sense) but a symbol rather... so we need to_s
98
+ attr = opts[:column] ? opts[:column] : :"#{klass}_id"
99
+ field attr
100
+ define_method(klass) { klass.to_s.camelize.constantize.first :id => send(attr) }
101
+ end
102
+
103
+ def has_one(klass, opts = {}) # This isn't really a klass (in the traditional sense) but a symbol rather... so we need to_s
104
+ attr = opts[:column] ? opts[:column] : :"#{name.downcase}_id"
105
+ define_method(klass) { klass.to_s.camelize.constantize.first attr => primary }
106
+ end
107
+
108
+ def has_many(klass, opts = {}) # This isn't really a klass (in the traditional sense) but a symbol rather... so we need to_s
109
+ attr = opts[:column] ? opts[:column] : :"#{name.downcase}_id"
110
+ define_method(klass.to_s.pluralize.to_sym) { klass.to_s.singularize.camelize.constantize.all attr => primary }
111
+ end
112
+
113
+ def delete(id)
114
+ response = RouteNGN.delete base_url, :id => id
115
+ response.success?
116
+ end
117
+
118
+ def find(*args)
119
+ case args.first
120
+ when :all
121
+ all(*args[1..-1])
122
+ else
123
+ first(*args[1..-1])
124
+ end
125
+ end
126
+
127
+ def all(opts = {})
128
+ result = []
129
+
130
+ response = RouteNGN.get base_url, opts
131
+
132
+ data = response['data']
133
+
134
+ return [new_from_saved(data)] unless data.is_a? Array
135
+
136
+ data.each do |d|
137
+ result << new_from_saved(d)
138
+ end
139
+
140
+ result
141
+ end
142
+
143
+ def first(opts = {})
144
+ all(opts).first # optimize/simplify?
145
+ end
146
+ end # ClassMethods
147
+
148
+ class PrimaryFieldException < Exception
149
+ def initialize(msg = "Model does not specify a primary field.")
150
+ super
151
+ end
152
+ end
153
+
154
+ class MultiplePrimaryFieldsException < Exception
155
+ def initialize(msg = "Model specifies too many primary fields.")
156
+ super
157
+ end
158
+ end
159
+ end # RouteNGN
@@ -0,0 +1,8 @@
1
+ class Carrier
2
+ include RouteNGN::Mapper
3
+
4
+ field :id, :primary => true
5
+ field :name
6
+
7
+ has_many :groups
8
+ end
@@ -0,0 +1,8 @@
1
+ class Dialcode
2
+ include RouteNGN::Mapper
3
+
4
+ field :id, :primary => true
5
+ field :digits
6
+
7
+ belongs_to :locale
8
+ end
@@ -0,0 +1,8 @@
1
+ class Endpoint
2
+ include RouteNGN::Mapper
3
+
4
+ field :id, :primary => true
5
+ field :ip
6
+
7
+ belongs_to :group, :column => :epgroup_id
8
+ end
@@ -0,0 +1,31 @@
1
+ class Group
2
+ include RouteNGN::Mapper
3
+
4
+ #TODO: fixure out a fix for abnormal foreign key 'epgroup_id'
5
+
6
+ field :id, :primary => true
7
+ field :name
8
+ field :direction
9
+ field :margin
10
+ field :prefix
11
+ field :strip_digits
12
+ field :rn_prefix
13
+ field :add_digits
14
+ field :add_field
15
+ field :add_type
16
+ field :blocked_region_id
17
+ field :route_on_type_id
18
+ field :remove_rn
19
+ field :trunkgroup
20
+ field :active_begin_day
21
+ field :active_end_day
22
+ field :active_begin_time
23
+ field :active_end_time
24
+
25
+ belongs_to :instance
26
+ belongs_to :carrier
27
+
28
+ has_many :endpoints, :column => :epgroup_id
29
+ has_many :rates
30
+
31
+ end
@@ -0,0 +1,21 @@
1
+ class Instance
2
+ include RouteNGN::Mapper
3
+
4
+ field :id, :primary => true
5
+ field :name
6
+
7
+ belongs_to :type
8
+
9
+ # custom method
10
+ # TODO make this more generic... possibly add functionality to the mapper that will allow for custom actions
11
+ def generate(groups)
12
+ groups_params = groups.inject({}) do |params, (k, v)|
13
+ params["groups[#{k}]"] = 1
14
+ params
15
+ end
16
+
17
+ response = RouteNGN.put self.class.base_url, {:id => self.id}.merge!(groups_params)
18
+ response.success?
19
+ end
20
+
21
+ end
@@ -0,0 +1,11 @@
1
+ class Locale
2
+ include RouteNGN::Mapper
3
+
4
+ field :id, :primary => true
5
+ field :name
6
+
7
+ belongs_to :region
8
+
9
+ has_many :dialcodes
10
+ has_many :rates
11
+ end
@@ -0,0 +1,11 @@
1
+ class Rate
2
+ include RouteNGN::Mapper
3
+ include RouteNGN::Uploader
4
+
5
+ field :id, :primary => true
6
+ field :rate
7
+
8
+ belongs_to :type
9
+ belongs_to :locale
10
+ belongs_to :group, :column => :epgroup_id
11
+ end
@@ -0,0 +1,17 @@
1
+ class Record
2
+ include RouteNGN::Mapper
3
+
4
+ field :dialcode, :primary => true
5
+ field :account_id
6
+ field :rn
7
+ field :locale_id
8
+ field :sent
9
+ field :received
10
+ field :detail
11
+ field :server
12
+ field :response_time
13
+ field :created_at_in_seconds
14
+ field :created_at
15
+ field :updated_at
16
+
17
+ end
@@ -0,0 +1,9 @@
1
+ class Region
2
+ include RouteNGN::Mapper
3
+ include RouteNGN::Uploader
4
+
5
+ field :id, :primary => true
6
+ field :name
7
+
8
+ has_many :locales
9
+ end
@@ -0,0 +1,8 @@
1
+ class Route
2
+ include RouteNGN::Mapper
3
+
4
+ field :id, :primary => true
5
+
6
+ belongs_to :instance
7
+ belongs_to :type
8
+ end
@@ -0,0 +1,7 @@
1
+ class Type
2
+ include RouteNGN::Mapper
3
+
4
+ field :id, :primary => true
5
+ field :name
6
+
7
+ end
@@ -0,0 +1,17 @@
1
+ module RouteNGN
2
+ module Uploader
3
+ def self.included(model)
4
+ model.class_eval do
5
+ extend ClassMethods
6
+ end
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+ def upload(file, opts = {})
12
+ args = {:uploaded_data => File.new(file)}.merge! opts
13
+ data, headers = HTTP::Multipart.prepare_query(args)
14
+ RouteNGN.upload("/upload#{base_url}", data, headers)
15
+ end
16
+ end
17
+ end
data/lib/routengn.rb ADDED
@@ -0,0 +1,151 @@
1
+ require 'rubygems'
2
+ gem 'oauth'
3
+ require 'oauth/consumer'
4
+ require 'json'
5
+
6
+ $: << File.expand_path(File.join File.dirname(__FILE__), 'routengn')
7
+ require 'mime/types'
8
+ require 'cgi'
9
+ require 'http/multipart'
10
+ require 'mapper'
11
+ require 'uploader'
12
+ require 'active_support'
13
+
14
+ module RouteNGN
15
+ Dir.glob(File.dirname(__FILE__) + "/routengn/models/*.rb").each do |model|
16
+ require File.expand_path(model)
17
+ end
18
+
19
+ CONF_FILE = File.join ENV['HOME'], ".ngnconf"
20
+
21
+ class Connection
22
+ attr_accessor :access_token
23
+
24
+ def initialize(*args)
25
+ @site, @key, @secret = args.empty? ? file_config : args
26
+ @consumer = OAuth::Consumer.new @key, @secret, {
27
+ :signature_method => 'HMAC-SHA1',
28
+ :site => @site
29
+ }
30
+
31
+ @request_token = @consumer.get_request_token
32
+ @consumer.request(:get, @request_token.authorize_url)
33
+
34
+ @access_token = @request_token.get_access_token
35
+ @access_token.consumer.http.read_timeout = 5000
36
+ end
37
+
38
+ def to_json
39
+ {:site => @site, :key => @key, :secret => @secret}.to_json
40
+ end
41
+
42
+ private
43
+ def file_config
44
+ data = File.open(CONF_FILE).read
45
+ data = JSON.parse data
46
+ [data['site'], data['key'], data['secret']]
47
+ end
48
+ end
49
+
50
+ class << self
51
+ def connect!(*args)
52
+ @connection = Connection.new *args
53
+ end
54
+
55
+ def connection
56
+ @connection
57
+ end
58
+
59
+ def write_config
60
+ File.open(CONF_FILE, 'w') do |f|
61
+ f << connection.to_json
62
+ end
63
+ end
64
+
65
+ [:get, :delete].each do |http_method|
66
+ define_method(http_method) do |*args|
67
+ uri, params = args
68
+ params ||= {}
69
+ #TODO remove the trailing '&' from url
70
+ url = params.inject("#{uri}?") do |_, (k, v)|
71
+ if v.is_a? Hash # Handle nesting of depth 1
72
+ _ << v.collect { |nested_k, nested_v| "#{k}[#{nested_k}]=#{nested_v}&" }.join
73
+ else
74
+ _ << "#{k}=#{v}&"
75
+ end
76
+ end
77
+ check_connection!
78
+ raw = @connection.access_token.send http_method, url
79
+ Response.new raw
80
+ end
81
+ end
82
+
83
+ [:post, :put].each do |http_method|
84
+ define_method(http_method) do |*args|
85
+ uri, params = args
86
+ params ||= {}
87
+ params = params.inject({}) do |_, (k, v)|
88
+ _[k.to_s] = v.to_s
89
+ _
90
+ end
91
+ check_connection!
92
+ raw = @connection.access_token.send http_method, uri, params
93
+ Response.new raw
94
+ end
95
+ end
96
+
97
+ define_method :upload do |*args|
98
+ uri, data, headers = args
99
+ raw = @connection.access_token.send :post, uri, data, headers
100
+ end
101
+
102
+ # TODO: find a better place to do this
103
+ def apply!
104
+ response = post '/apply'
105
+ response.success?
106
+ end
107
+
108
+ private
109
+ def check_connection!
110
+ raise ConnectionException.new unless @connection
111
+ raise OAuthException.new unless @connection.access_token
112
+ end
113
+ end
114
+
115
+ # TODO: validation errors are now stored in failure message's data... we should figure out what to do with it
116
+ class Response
117
+ SUCCESS_RANGE = (200..399)
118
+
119
+ attr_reader :data, :status
120
+
121
+ def initialize(raw)
122
+ @status = raw.code.to_i
123
+ # TODO: if it's not a success code we probably shouldn't bother parsing the JSON
124
+ @data = JSON.parse raw.body
125
+ end
126
+
127
+ def [](key)
128
+ @data[key]
129
+ end
130
+
131
+ def success?
132
+ SUCCESS_RANGE.include? @status
133
+ end
134
+
135
+ def failure?
136
+ !success?
137
+ end
138
+ end
139
+
140
+ class ConnectionException < Exception
141
+ def initialize(msg = "No connection to RouteNGN established.")
142
+ super
143
+ end
144
+ end
145
+
146
+ class OAuthException < Exception
147
+ def initialize(msg = "Invalid RouteNGN OAuth credentials.")
148
+ super
149
+ end
150
+ end
151
+ end
data/routengn.gemspec ADDED
@@ -0,0 +1,76 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{routengn}
8
+ s.version = "0.2.9"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Keith Larrimore", "Quin Hoxie"]
12
+ s.date = %q{2009-09-30}
13
+ s.default_executable = %q{ngncmd}
14
+ s.description = %q{RouteNGN is an Advanced SIP Redirect Routing Platform.}
15
+ s.email = %q{keithlarrimore+github@gmail.com}
16
+ s.executables = ["ngncmd"]
17
+ s.extra_rdoc_files = [
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ "README.rdoc",
22
+ "bin/ngncmd",
23
+ "docs/RouteNGN-Gems-Install.doc",
24
+ "examples/cli.rb",
25
+ "examples/provision.rb",
26
+ "lib/routengn.rb",
27
+ "lib/routengn/console.rb",
28
+ "lib/routengn/http/multipart.rb",
29
+ "lib/routengn/mapper.rb",
30
+ "lib/routengn/models/carrier.rb",
31
+ "lib/routengn/models/dialcode.rb",
32
+ "lib/routengn/models/endpoint.rb",
33
+ "lib/routengn/models/group.rb",
34
+ "lib/routengn/models/instance.rb",
35
+ "lib/routengn/models/locale.rb",
36
+ "lib/routengn/models/rate.rb",
37
+ "lib/routengn/models/record.rb",
38
+ "lib/routengn/models/region.rb",
39
+ "lib/routengn/models/route.rb",
40
+ "lib/routengn/models/type.rb",
41
+ "lib/routengn/uploader.rb",
42
+ "routengn.gemspec",
43
+ "script/console"
44
+ ]
45
+ s.homepage = %q{http://github.com/klarrimore/routengn}
46
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
47
+ s.require_paths = ["lib"]
48
+ s.rubygems_version = %q{1.3.1}
49
+ s.summary = %q{RouteNGN is an Advanced SIP Redirect Routing Platform.}
50
+ s.test_files = [
51
+ "examples/provision.rb",
52
+ "examples/cli.rb"
53
+ ]
54
+
55
+ if s.respond_to? :specification_version then
56
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
57
+ s.specification_version = 2
58
+
59
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
60
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
61
+ s.add_runtime_dependency(%q<mime-types>, [">= 0"])
62
+ s.add_runtime_dependency(%q<json>, [">= 0"])
63
+ s.add_runtime_dependency(%q<oauth>, [">= 0"])
64
+ else
65
+ s.add_dependency(%q<activesupport>, [">= 0"])
66
+ s.add_dependency(%q<mime-types>, [">= 0"])
67
+ s.add_dependency(%q<json>, [">= 0"])
68
+ s.add_dependency(%q<oauth>, [">= 0"])
69
+ end
70
+ else
71
+ s.add_dependency(%q<activesupport>, [">= 0"])
72
+ s.add_dependency(%q<mime-types>, [">= 0"])
73
+ s.add_dependency(%q<json>, [">= 0"])
74
+ s.add_dependency(%q<oauth>, [">= 0"])
75
+ end
76
+ end
data/script/console ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+
6
+ exec "#{irb} -I \"#{File.dirname(__FILE__)}/../lib\" -r \"routengn\" -I \"#{File.dirname(__FILE__)}/../lib/routengn\" -r \"console\" --simple-prompt"
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: routengn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.9
5
+ platform: ruby
6
+ authors:
7
+ - Keith Larrimore
8
+ - Quin Hoxie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-09-30 00:00:00 -04:00
14
+ default_executable: ngncmd
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: mime-types
28
+ type: :runtime
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ version:
36
+ - !ruby/object:Gem::Dependency
37
+ name: json
38
+ type: :runtime
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ - !ruby/object:Gem::Dependency
47
+ name: oauth
48
+ type: :runtime
49
+ version_requirement:
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ description: RouteNGN is an Advanced SIP Redirect Routing Platform.
57
+ email: keithlarrimore+github@gmail.com
58
+ executables:
59
+ - ngncmd
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - README.rdoc
64
+ files:
65
+ - README.rdoc
66
+ - bin/ngncmd
67
+ - docs/RouteNGN-Gems-Install.doc
68
+ - examples/cli.rb
69
+ - examples/provision.rb
70
+ - lib/routengn.rb
71
+ - lib/routengn/console.rb
72
+ - lib/routengn/http/multipart.rb
73
+ - lib/routengn/mapper.rb
74
+ - lib/routengn/models/carrier.rb
75
+ - lib/routengn/models/dialcode.rb
76
+ - lib/routengn/models/endpoint.rb
77
+ - lib/routengn/models/group.rb
78
+ - lib/routengn/models/instance.rb
79
+ - lib/routengn/models/locale.rb
80
+ - lib/routengn/models/rate.rb
81
+ - lib/routengn/models/record.rb
82
+ - lib/routengn/models/region.rb
83
+ - lib/routengn/models/route.rb
84
+ - lib/routengn/models/type.rb
85
+ - lib/routengn/uploader.rb
86
+ - routengn.gemspec
87
+ - script/console
88
+ has_rdoc: true
89
+ homepage: http://github.com/klarrimore/routengn
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options:
94
+ - --inline-source
95
+ - --charset=UTF-8
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "0"
103
+ version:
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: "0"
109
+ version:
110
+ requirements: []
111
+
112
+ rubyforge_project:
113
+ rubygems_version: 1.3.5
114
+ signing_key:
115
+ specification_version: 2
116
+ summary: RouteNGN is an Advanced SIP Redirect Routing Platform.
117
+ test_files:
118
+ - examples/provision.rb
119
+ - examples/cli.rb