point 1.0.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.
- data/lib/point.rb +27 -0
- data/lib/point/base.rb +160 -0
- data/lib/point/errors.rb +20 -0
- data/lib/point/request.rb +63 -0
- data/lib/point/zone.rb +24 -0
- data/lib/point/zone_record.rb +23 -0
- metadata +69 -0
data/lib/point.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
## Require dependencies. All network traffic is processed using net/http and
|
2
|
+
## is transfered using JSON.
|
3
|
+
require 'json'
|
4
|
+
require 'uri'
|
5
|
+
require 'net/http'
|
6
|
+
|
7
|
+
## PointHQ Ruby API Client
|
8
|
+
## This is a ruby API client for the Point DNS hosting service.
|
9
|
+
|
10
|
+
require 'point/request'
|
11
|
+
require 'point/errors'
|
12
|
+
require 'point/base'
|
13
|
+
require 'point/zone'
|
14
|
+
require 'point/zone_record'
|
15
|
+
|
16
|
+
module Point
|
17
|
+
VERSION = '1.0.0'
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_accessor :username
|
21
|
+
attr_accessor :apitoken
|
22
|
+
attr_accessor :site
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
Point.site = "http://pointhq.com"
|
data/lib/point/base.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
module Point
|
2
|
+
class Base
|
3
|
+
|
4
|
+
## Store all attributes for the model we're working with.
|
5
|
+
attr_accessor :id, :attributes, :errors
|
6
|
+
|
7
|
+
## Pass any methods via. the attributes hash to see if they exist
|
8
|
+
## before resuming normal method_missing behaviour
|
9
|
+
def method_missing(method, *params)
|
10
|
+
set = method.to_s.include?('=')
|
11
|
+
key = method.to_s.sub('=', '')
|
12
|
+
self.attributes = Hash.new unless self.attributes.is_a?(Hash)
|
13
|
+
if set
|
14
|
+
self.attributes[key] = params.first
|
15
|
+
else
|
16
|
+
self.attributes[key]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
## Find a record or set of records. Passing :all will return all records and passing an integer
|
23
|
+
## will return the individual record for the ID passed.
|
24
|
+
def find(type, params = {})
|
25
|
+
case type
|
26
|
+
when :all then find_all(params)
|
27
|
+
when Integer then find_single(type, params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
## Find all objects and return an array of objects with the attributes set.
|
32
|
+
def find_all(params)
|
33
|
+
JSON.parse(Request.new(collection_path(params)).make.output).map do |o|
|
34
|
+
create_object(o[class_name.downcase], params)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
## Find a single object and return an object for it.
|
39
|
+
def find_single(id, params = {})
|
40
|
+
o = JSON.parse(Request.new(member_path(id, params)).make.output)
|
41
|
+
if o[class_name.downcase]
|
42
|
+
create_object(o[class_name.downcase], params)
|
43
|
+
else
|
44
|
+
raise Point::Errors::NotFound, "Record not found"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
## Post to the specified object on the collection path
|
49
|
+
def post(path)
|
50
|
+
Request.new(path.to_s, :post).make
|
51
|
+
end
|
52
|
+
|
53
|
+
## Return the collection path for this model. Very lazy pluralizion here
|
54
|
+
## at the moment, nothing in Point needs to be pluralized with anything
|
55
|
+
## other than just adding an 's'.
|
56
|
+
def collection_path(params = {})
|
57
|
+
class_name.downcase + 's'
|
58
|
+
end
|
59
|
+
|
60
|
+
## Return the member path for the passed ID & attributes
|
61
|
+
def member_path(id, params = {})
|
62
|
+
[collection_path, id].join('/')
|
63
|
+
end
|
64
|
+
|
65
|
+
## Return the point class name
|
66
|
+
def class_name
|
67
|
+
self.name.to_s.split('::').last.downcase
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
## Create a new object with the specified attributes and getting and ID.
|
73
|
+
## Returns the newly created object
|
74
|
+
def create_object(attributes, objects = [])
|
75
|
+
o = self.new
|
76
|
+
o.attributes = attributes
|
77
|
+
o.id = attributes['id']
|
78
|
+
for key, object in objects.select{|k,v| v.kind_of?(Point::Base)}
|
79
|
+
o.attributes[key.to_s] = object
|
80
|
+
end
|
81
|
+
o
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
## Run a post on the member path. Returns the ouput from the post, false if a conflict or raises
|
86
|
+
## a Point::Error. Optionally, pass a second 'data' parameter to send data to the post action.
|
87
|
+
def post(action, data = nil)
|
88
|
+
path = self.class.member_path(self.id, default_params) + "/" + action.to_s
|
89
|
+
request = Request.new(path, :post)
|
90
|
+
request.data = data
|
91
|
+
request.make
|
92
|
+
end
|
93
|
+
|
94
|
+
## Delete this record from the remote service. Returns true or false depending on the success
|
95
|
+
## status of the destruction.
|
96
|
+
def destroy
|
97
|
+
Request.new(self.class.member_path(self.id, default_params), :delete).make.success?
|
98
|
+
end
|
99
|
+
|
100
|
+
def new_record?
|
101
|
+
self.id.nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
def save
|
105
|
+
new_record? ? create : update
|
106
|
+
end
|
107
|
+
|
108
|
+
def create
|
109
|
+
request = Request.new(self.class.collection_path(default_params), :post)
|
110
|
+
request.data = {self.class.class_name.downcase.to_sym => attributes_to_post}
|
111
|
+
if request.make && request.success?
|
112
|
+
new_record = JSON.parse(request.output)[self.class.class_name]
|
113
|
+
self.id = new_record['id']
|
114
|
+
true
|
115
|
+
else
|
116
|
+
populate_errors(request.output)
|
117
|
+
false
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
## Push the updated attributes to the remote. Returns true if the record was saved successfully
|
122
|
+
## other false if not. If not saved successfully, the errors hash will be updated with an array
|
123
|
+
## of all errors with the submission.
|
124
|
+
def update
|
125
|
+
request = Request.new(self.class.member_path(self.id, default_params), :put)
|
126
|
+
request.data = {self.class.class_name.downcase.to_sym => attributes_to_post}
|
127
|
+
if request.make && request.success?
|
128
|
+
true
|
129
|
+
else
|
130
|
+
populate_errors(request.output)
|
131
|
+
false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
## Populate the errors hash from the given raw JSON output
|
138
|
+
def populate_errors(json)
|
139
|
+
self.errors = Hash.new
|
140
|
+
JSON.parse(json).inject(self.errors) do |r, e|
|
141
|
+
r[e.first] = e.last
|
142
|
+
r
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
## An array of params which should always be sent with this instances requests
|
147
|
+
def default_params
|
148
|
+
Hash.new
|
149
|
+
end
|
150
|
+
|
151
|
+
## Attributes which can be passed for update & creation
|
152
|
+
def attributes_to_post
|
153
|
+
self.attributes.inject(Hash.new) do |r,(key,value)|
|
154
|
+
r[key] = value if value.is_a?(String) || value.is_a?(Integer) || value.is_a?(Fixnum)
|
155
|
+
r
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
data/lib/point/errors.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Point
|
2
|
+
|
3
|
+
## Base level error which all other point errors will inherit from. It may also be
|
4
|
+
## invoked for errors which don't directly relate to other errors below.
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
module Errors
|
8
|
+
|
9
|
+
## The service is currently unavailable. This may be caused by rate limiting or the API
|
10
|
+
## or the service has been disabled by the system
|
11
|
+
class ServiceUnavailable < Error; end
|
12
|
+
|
13
|
+
## Access was denied to the remote service
|
14
|
+
class AccessDenied < Error; end
|
15
|
+
|
16
|
+
## A communication error occured while talking to the Point API
|
17
|
+
class CommunicationError < Error; end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Point
|
2
|
+
class Request
|
3
|
+
|
4
|
+
attr_reader :path, :method
|
5
|
+
attr_accessor :data
|
6
|
+
|
7
|
+
def initialize(path, method = :get)
|
8
|
+
@path = path
|
9
|
+
@method = method
|
10
|
+
end
|
11
|
+
|
12
|
+
def success?
|
13
|
+
@success || false
|
14
|
+
end
|
15
|
+
|
16
|
+
def output
|
17
|
+
@output || nil
|
18
|
+
end
|
19
|
+
|
20
|
+
## Make a request to the Point API using net/http. Data passed can be a hash or a string
|
21
|
+
## Hashes will be converted to JSON before being sent to the remote service.
|
22
|
+
def make
|
23
|
+
uri = URI.parse([Point.site, @path].join('/'))
|
24
|
+
http_request = http_class.new(uri.path)
|
25
|
+
http_request.basic_auth(Point.username, Point.apitoken)
|
26
|
+
http_request.add_field("Accept", "application/json")
|
27
|
+
http_request.add_field("Content-type", "application/json")
|
28
|
+
|
29
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
data = self.data.to_json if self.data.is_a?(Hash) && self.data.respond_to?(:to_json)
|
31
|
+
http_result = http.request(http_request, data)
|
32
|
+
@output = http_result.body
|
33
|
+
@success = case http_result
|
34
|
+
when Net::HTTPSuccess
|
35
|
+
true
|
36
|
+
when Net::HTTPServiceUnavailable
|
37
|
+
raise Point::Errors::ServiceUnavailable
|
38
|
+
when Net::HTTPForbidden, Net::HTTPUnauthorized
|
39
|
+
raise Point::Errors::AccessDenied, "Access Denied for '#{Point.username}'"
|
40
|
+
when Net::HTTPNotFound
|
41
|
+
raise Point::Errors::CommunicationError, "Not Found at #{uri.to_s}"
|
42
|
+
when Net::HTTPClientError
|
43
|
+
false
|
44
|
+
else
|
45
|
+
raise Point::Errors::CommunicationError, http_result.body
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def http_class
|
53
|
+
case @method
|
54
|
+
when :post then Net::HTTP::Post
|
55
|
+
when :put then Net::HTTP::Put
|
56
|
+
when :delete then Net::HTTP::Delete
|
57
|
+
else
|
58
|
+
Net::HTTP::Get
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
data/lib/point/zone.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Point
|
2
|
+
class Zone < Base
|
3
|
+
|
4
|
+
def apply!
|
5
|
+
post(:apply)
|
6
|
+
end
|
7
|
+
|
8
|
+
def records
|
9
|
+
ZoneRecord.find(:all, :zone => self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def record(id)
|
13
|
+
ZoneRecord.find(id, :zone => self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_record(attributes = {})
|
17
|
+
z = ZoneRecord.new
|
18
|
+
z.attributes = attributes if attributes.is_a?(Hash)
|
19
|
+
z.zone = self
|
20
|
+
z
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Point
|
2
|
+
class ZoneRecord < Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def collection_path(params = {})
|
6
|
+
"zones/#{params[:zone].id}/records"
|
7
|
+
end
|
8
|
+
|
9
|
+
def member_path(id, params = {})
|
10
|
+
"zones/#{params[:zone].id}/records/#{id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def class_name
|
14
|
+
"zone_record"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_params
|
19
|
+
{:zone => self.zone}
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: point
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Cooke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-29 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.1.5
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: adam@atechmedia.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- lib/point/base.rb
|
35
|
+
- lib/point/errors.rb
|
36
|
+
- lib/point/request.rb
|
37
|
+
- lib/point/zone.rb
|
38
|
+
- lib/point/zone_record.rb
|
39
|
+
- lib/point.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://www.pointhq.com
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.3.5
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: API client for the PointHQ DNS Hosting System
|
68
|
+
test_files: []
|
69
|
+
|