klarrimore-routengn 0.22 → 0.23
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/http/multipart.rb +80 -0
- data/lib/routengn.rb +108 -0
- data/lib/routengn/console.rb +18 -0
- data/lib/routengn/mapper.rb +152 -0
- data/lib/routengn/models/carrier.rb +8 -0
- data/lib/routengn/models/dialcode.rb +8 -0
- data/lib/routengn/models/endpoint.rb +8 -0
- data/lib/routengn/models/group.rb +31 -0
- data/lib/routengn/models/instance.rb +9 -0
- data/lib/routengn/models/locale.rb +11 -0
- data/lib/routengn/models/rate.rb +11 -0
- data/lib/routengn/models/record.rb +15 -0
- data/lib/routengn/models/region.rb +9 -0
- data/lib/routengn/models/route.rb +5 -0
- data/lib/routengn/models/type.rb +7 -0
- data/lib/routengn/uploader.rb +16 -0
- metadata +20 -1
@@ -0,0 +1,80 @@
|
|
1
|
+
# Takes a hash of string and file parameters and returns a string of text
|
2
|
+
# formatted to be sent as a multipart form post.
|
3
|
+
#
|
4
|
+
# Author:: Cody Brimhall <mailto:cbrimhall@ucdavis.edu>
|
5
|
+
# Created:: 22 Feb 2008
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'mime/types'
|
9
|
+
require 'cgi'
|
10
|
+
|
11
|
+
|
12
|
+
module Multipart
|
13
|
+
VERSION = "1.0.0" unless const_defined?(:VERSION)
|
14
|
+
|
15
|
+
# Formats a given hash as a multipart form post
|
16
|
+
# If a hash value responds to :string or :read messages, then it is
|
17
|
+
# interpreted as a file and processed accordingly; otherwise, it is assumed
|
18
|
+
# to be a string
|
19
|
+
class Post
|
20
|
+
# We have to pretend like we're a web browser...
|
21
|
+
USERAGENT = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6" unless const_defined?(:USERAGENT)
|
22
|
+
BOUNDARY = "0123456789ABLEWASIEREISAWELBA9876543210" unless const_defined?(:BOUNDARY)
|
23
|
+
CONTENT_TYPE = "multipart/form-data; boundary=#{ BOUNDARY }" unless const_defined?(:CONTENT_TYPE)
|
24
|
+
HEADER = { "Content-Type" => CONTENT_TYPE, "User-Agent" => USERAGENT } unless const_defined?(:HEADER)
|
25
|
+
|
26
|
+
def self.prepare_query(params)
|
27
|
+
fp = []
|
28
|
+
|
29
|
+
params.each do |k, v|
|
30
|
+
# Are we trying to make a file parameter?
|
31
|
+
if v.respond_to?(:path) and v.respond_to?(:read) then
|
32
|
+
fp.push(FileParam.new(k, v.path, v.read))
|
33
|
+
# We must be trying to make a regular parameter
|
34
|
+
else
|
35
|
+
fp.push(StringParam.new(k, v))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Assemble the request body using the special multipart format
|
40
|
+
query = fp.collect {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("") + "--" + BOUNDARY + "--"
|
41
|
+
return query, HEADER
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Formats a basic string key/value pair for inclusion with a multipart post
|
48
|
+
class StringParam
|
49
|
+
attr_accessor :k, :v
|
50
|
+
|
51
|
+
def initialize(k, v)
|
52
|
+
@k = k
|
53
|
+
@v = v
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_multipart
|
57
|
+
return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"\r\n\r\n#{v}\r\n"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Formats the contents of a file or string for inclusion with a multipart
|
62
|
+
# form post
|
63
|
+
class FileParam
|
64
|
+
attr_accessor :k, :filename, :content
|
65
|
+
|
66
|
+
def initialize(k, filename, content)
|
67
|
+
@k = k
|
68
|
+
@filename = filename
|
69
|
+
@content = content
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_multipart
|
73
|
+
# If we can tell the possible mime-type from the filename, use the
|
74
|
+
# first in the list; otherwise, use "application/octet-stream"
|
75
|
+
mime_type = MIME::Types.type_for(filename)[0] || MIME::Types["application/octet-stream"][0]
|
76
|
+
return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"; filename=\"#{ filename }\"\r\n" +
|
77
|
+
"Content-Type: #{ mime_type.simplified }\r\n\r\n#{ content }\r\n"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/routengn.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'oauth'
|
3
|
+
require 'oauth/consumer'
|
4
|
+
require 'json'
|
5
|
+
require 'http/multipart'
|
6
|
+
require 'routengn/mapper'
|
7
|
+
require 'routengn/uploader'
|
8
|
+
require 'active_support'
|
9
|
+
|
10
|
+
module RouteNGN
|
11
|
+
Dir.glob(File.dirname(__FILE__) + "/routengn/models/*.rb").each do |model|
|
12
|
+
require File.expand_path(model)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Connection
|
16
|
+
attr_accessor :access_token
|
17
|
+
|
18
|
+
def initialize(site, key, secret)
|
19
|
+
@consumer = OAuth::Consumer.new key, secret, {
|
20
|
+
:signature_method => 'HMAC-SHA1',
|
21
|
+
:site => site
|
22
|
+
}
|
23
|
+
|
24
|
+
@request_token = @consumer.get_request_token
|
25
|
+
@consumer.request(:get, @request_token.authorize_url)
|
26
|
+
|
27
|
+
@access_token = @request_token.get_access_token
|
28
|
+
@access_token.consumer.http.read_timeout = 5000
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def connect!(site, key, secret)
|
34
|
+
@connection = Connection.new site, key, secret
|
35
|
+
end
|
36
|
+
|
37
|
+
def connection
|
38
|
+
@connection
|
39
|
+
end
|
40
|
+
|
41
|
+
[:get, :delete].each do |http_method|
|
42
|
+
define_method(http_method) do |*args|
|
43
|
+
uri, params = args
|
44
|
+
params ||= {}
|
45
|
+
url = params.inject("#{uri}?") { |_, (k, v)| _ << "#{k}=#{v}" }
|
46
|
+
raw = @connection.access_token.send http_method, url
|
47
|
+
Response.new raw
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
[:post, :put].each do |http_method|
|
52
|
+
define_method(http_method) do |*args|
|
53
|
+
uri, params = args
|
54
|
+
params ||= {}
|
55
|
+
params = params.inject({}) do |_, (k, v)|
|
56
|
+
_[k.to_s] = v.to_s
|
57
|
+
_
|
58
|
+
end
|
59
|
+
check_connection!
|
60
|
+
raw = @connection.access_token.send http_method, uri, params
|
61
|
+
Response.new raw
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def check_connection!
|
67
|
+
raise ConnectionException.new unless @connection
|
68
|
+
raise OAuthException.new unless @connection.access_token
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# TODO: validation errors are now stored in failure message's data... we should figure out what to do with it
|
73
|
+
class Response
|
74
|
+
SUCCESS_RANGE = (200..399)
|
75
|
+
|
76
|
+
attr_reader :data, :status
|
77
|
+
|
78
|
+
def initialize(raw)
|
79
|
+
@status = raw.code.to_i
|
80
|
+
# TODO: if it's not a success code we probably shouldn't bother parsing the JSON
|
81
|
+
@data = JSON.parse raw.body
|
82
|
+
end
|
83
|
+
|
84
|
+
def [](key)
|
85
|
+
@data[key]
|
86
|
+
end
|
87
|
+
|
88
|
+
def success?
|
89
|
+
SUCCESS_RANGE.include? @status
|
90
|
+
end
|
91
|
+
|
92
|
+
def failure?
|
93
|
+
!success?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class ConnectionException < Exception
|
98
|
+
def initialize(msg = "No connection to RouteNGN established.")
|
99
|
+
super
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class OAuthException < Exception
|
104
|
+
def initialize(msg = "Invalid RouteNGN OAuth credentials.")
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -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,152 @@
|
|
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] # TODO prevent multiple primaries
|
91
|
+
alias_method :primary, name
|
92
|
+
@primary_field = name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def belongs_to(klass, opts = {}) # This isn't really a klass (in the traditional sense) but a symbol rather... so we need to_s
|
97
|
+
attr = opts[:column] ? opts[:column] : :"#{klass}_id"
|
98
|
+
field attr
|
99
|
+
define_method(klass) { klass.to_s.camelize.constantize.first :id => send(attr) }
|
100
|
+
end
|
101
|
+
|
102
|
+
def has_one(klass, opts = {}) # This isn't really a klass (in the traditional sense) but a symbol rather... so we need to_s
|
103
|
+
attr = opts[:column] ? opts[:column] : :"#{name}_id"
|
104
|
+
define_method(klass) { klass.to_s.camelize.constantize.first attr => primary }
|
105
|
+
end
|
106
|
+
|
107
|
+
def has_many(klass, opts = {}) # This isn't really a klass (in the traditional sense) but a symbol rather... so we need to_s
|
108
|
+
attr = opts[:column] ? opts[:column] : :"#{name}_id"
|
109
|
+
define_method(klass.to_s.pluralize.to_sym) { klass.to_s.singularize.camelize.constantize.all attr => primary }
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete(id)
|
113
|
+
response = RouteNGN.delete base_url, :id => id
|
114
|
+
response.success?
|
115
|
+
end
|
116
|
+
|
117
|
+
def find(*args)
|
118
|
+
case args.first
|
119
|
+
when :all
|
120
|
+
all(*args[1..-1])
|
121
|
+
else
|
122
|
+
first(*args[1..-1])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def all(opts = {})
|
127
|
+
result = []
|
128
|
+
|
129
|
+
response = RouteNGN.get base_url, opts
|
130
|
+
|
131
|
+
data = response['data']
|
132
|
+
|
133
|
+
return [new_from_saved(data)] unless data.is_a? Array
|
134
|
+
|
135
|
+
data.each do |d|
|
136
|
+
result << new_from_saved(d)
|
137
|
+
end
|
138
|
+
|
139
|
+
result
|
140
|
+
end
|
141
|
+
|
142
|
+
def first(opts = {})
|
143
|
+
all(opts).first # optimize/simplify?
|
144
|
+
end
|
145
|
+
end # ClassMethods
|
146
|
+
|
147
|
+
class PrimaryFieldException < Exception
|
148
|
+
def initialize(msg = "Model does not specify a primary field.")
|
149
|
+
super
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end # RouteNGN
|
@@ -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,16 @@
|
|
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
|
12
|
+
# STUB
|
13
|
+
puts "data, headers = Multipart::Post.prepare_query(\"title\" => 'title', \"uploaded_data\" => File.new(path))"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: klarrimore-routengn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.23"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keith Larrimore
|
@@ -43,6 +43,25 @@ extra_rdoc_files: []
|
|
43
43
|
files:
|
44
44
|
- README.rdoc
|
45
45
|
- lib/
|
46
|
+
- lib/routengn.rb
|
47
|
+
- lib/http
|
48
|
+
- lib/http/multipart.rb
|
49
|
+
- lib/routengn
|
50
|
+
- lib/routengn/uploader.rb
|
51
|
+
- lib/routengn/console.rb
|
52
|
+
- lib/routengn/models
|
53
|
+
- lib/routengn/models/route.rb
|
54
|
+
- lib/routengn/models/locale.rb
|
55
|
+
- lib/routengn/models/endpoint.rb
|
56
|
+
- lib/routengn/models/carrier.rb
|
57
|
+
- lib/routengn/models/region.rb
|
58
|
+
- lib/routengn/models/rate.rb
|
59
|
+
- lib/routengn/models/dialcode.rb
|
60
|
+
- lib/routengn/models/instance.rb
|
61
|
+
- lib/routengn/models/record.rb
|
62
|
+
- lib/routengn/models/group.rb
|
63
|
+
- lib/routengn/models/type.rb
|
64
|
+
- lib/routengn/mapper.rb
|
46
65
|
has_rdoc: false
|
47
66
|
homepage: http://github.com/mojombo/grit
|
48
67
|
licenses:
|