enom-api 0.1.1

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,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Geoff Garside
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = eNom API Client
2
+
3
+ Provides a Ruby client for the eNom Reseller API.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Geoff Garside. See LICENSE for details.
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "enom-api"
8
+ gem.summary = %Q{eNom API Client}
9
+ gem.description = %Q{Client for communicating with the eNom API}
10
+ gem.email = "geoff@geoffgarside.co.uk"
11
+ gem.homepage = "http://github.com/geoffgarside/enom-api"
12
+ gem.authors = ["Geoff Garside"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_development_dependency "yard", ">= 0"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ gem.add_dependency "demolisher", ">= 0.6.0"
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/test_*.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :test => :check_dependencies
44
+
45
+ task :default => :test
46
+
47
+ begin
48
+ require 'yard'
49
+ YARD::Rake::YardocTask.new
50
+ rescue LoadError
51
+ task :yardoc do
52
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
53
+ end
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,61 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
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{enom-api}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Geoff Garside"]
12
+ s.date = %q{2011-03-29}
13
+ s.description = %q{Client for communicating with the eNom API}
14
+ s.email = %q{geoff@geoffgarside.co.uk}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "enom-api.gemspec",
26
+ "lib/enom-api.rb",
27
+ "lib/enom-api/client.rb",
28
+ "lib/enom-api/interface.rb",
29
+ "lib/enom-api/registrant.rb",
30
+ "lib/enom-api/search_query.rb",
31
+ "test/helper.rb",
32
+ "test/test_enom-api.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/geoffgarside/enom-api}
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.6.2}
37
+ s.summary = %q{eNom API Client}
38
+ s.test_files = [
39
+ "test/helper.rb",
40
+ "test/test_enom-api.rb"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
48
+ s.add_development_dependency(%q<yard>, [">= 0"])
49
+ s.add_runtime_dependency(%q<demolisher>, [">= 0.6.0"])
50
+ else
51
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ s.add_dependency(%q<yard>, [">= 0"])
53
+ s.add_dependency(%q<demolisher>, [">= 0.6.0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
57
+ s.add_dependency(%q<yard>, [">= 0"])
58
+ s.add_dependency(%q<demolisher>, [">= 0.6.0"])
59
+ end
60
+ end
61
+
@@ -0,0 +1,24 @@
1
+ require 'xml'
2
+ require 'demolisher'
3
+
4
+ module EnomAPI # :nodoc:
5
+ autoload :Client, File.dirname(__FILE__) + '/enom-api/client.rb'
6
+ autoload :Interface, File.dirname(__FILE__) + '/enom-api/interface.rb'
7
+ autoload :Registrant, File.dirname(__FILE__) + '/enom-api/registrant.rb'
8
+ autoload :SearchQuery, File.dirname(__FILE__) + '/enom-api/search_query.rb'
9
+
10
+ # API Response error exception
11
+ class ResponseError < RuntimeError
12
+ attr_reader :messages
13
+ def initialize(error_messages)
14
+ @messages = error_messages
15
+ end
16
+ end
17
+ # API Incomplete response error
18
+ class IncompleteResponseError < RuntimeError
19
+ attr_reader :xml
20
+ def initialize(xml)
21
+ @xml = xml
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,729 @@
1
+ require 'time'
2
+ require 'demolisher'
3
+
4
+ module EnomAPI
5
+ # Client
6
+ class Client
7
+ # @param [String] user eNom Account ID
8
+ # @param [String] passwd eNom Account Password
9
+ # @param [String] server Server to connect to. Use 'resellertest.enom.com' for test.
10
+ def initialize(user, passwd, server = 'reseller.enom.com')
11
+ @user, @server = user, server
12
+ @conn = Interface.new(user, passwd, server)
13
+ end
14
+ def inspect # :nodoc:
15
+ "#<#{self.class} #{@user}@#{@server}>"
16
+ end
17
+
18
+ # Perform a search.
19
+ #
20
+ # The returned array contains hashes with the following keys
21
+ # - (String) +:id+ -- Domain ID within the eNom registry
22
+ # - (String) +:name+ -- Domain name
23
+ # - (BOOL) +:auto_renew+ -- Whether auto-renew is set on the domain
24
+ # - (Time) +:expires+ -- Expiration date of the domain
25
+ # - (String) +:status+ -- Registration status of the domain
26
+ # - (Array) +:nameservers+ -- Nameserver names
27
+ #
28
+ # @yield [q] block to build up search query
29
+ # @yieldparam [SearchQuery] q SearchQuery instance
30
+ # @return [Array] Array of hashes of search results
31
+ # including domain ID, name, auto_renew status, expiration date
32
+ # status and nameservers
33
+ def search(&block)
34
+ response = @conn.search(&block)
35
+ xml = XML::Parser.string(response).parse
36
+
37
+ o = Hash.new
38
+ d = Demolisher.demolish(xml)
39
+ d.send("interface-response") do
40
+ d.DomainSearch do
41
+ o[:total_results] = d.TotalResults.to_s.to_i
42
+ o[:start_position] = d.StartPosition.to_s.to_i
43
+ o[:next_position] = d.NextPosition.to_s.to_i
44
+
45
+ d.Domains do
46
+ o[:results] = Array.new
47
+ d.Domain do
48
+ o[:results] << {
49
+ :id => d.DomainNameID,
50
+ :name => "#{d.SLD}.#{d.TLD}",
51
+ :auto_renew => d.AutoRenew?,
52
+ :expires => Time.parse(d.ExpDate),
53
+ :status => d.DomainRegistrationStatus,
54
+ :nameservers => (d.NameServers && d.NameServers.split(",")) }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ o
60
+ end
61
+
62
+ # Checks status of domain names
63
+ #
64
+ # @param [Array<String>] *names Names of domains to check the status of.
65
+ # @return [Boolean] when 1 name provided, whether the domain is available or not
66
+ # @return [Hash<String, Boolean>] when multiple names provided, hash of names
67
+ # and whether the domain is available or not
68
+ # @raise [ArgumentError] if more than 30 names are provided
69
+ def check(*names)
70
+ raise ArgumentError, "maximum number of names is 30" if names.size > 30
71
+ xml = send_recv(:Check, :DomainList => names.join(','))
72
+
73
+ info = (1..xml.DomainCount.to_i).map do |i|
74
+ [xml.send("Domain#{i}"), xml.send("RRPCode#{i}") == '210']
75
+ end.flatten
76
+
77
+ return info[1] if info.size == 2
78
+ Hash[*info]
79
+ end
80
+
81
+ # Checks the status of a nameserver registered with eNom.
82
+ #
83
+ # @param [String] name nameserver to check
84
+ # @return [Hash] the nameserver +:name+ and +:ipaddress+
85
+ # @return [false] the nameserver is not registered with eNom
86
+ def check_ns_status(name)
87
+ xml = send_recv(:CheckNSStatus, :CheckNSName => name)
88
+
89
+ return false if xml.RRPCode != '200'
90
+ { :name => xml.CheckNsStatus.name, :ipaddress => xml.CheckNsStatus.ipaddress }
91
+ end
92
+
93
+ # Delete a registered nameserver from eNom
94
+ #
95
+ # @param [String] name nameserver to delete from the registry
96
+ # @return [Boolean] +true+ if deleted successfully, +false+ if not
97
+ def delete_nameserver(name)
98
+ xml = send_recv(:DeleteNameServer, :NS => name)
99
+ return xml.RRPCode == '200' && xml.NsSuccess == '1'
100
+ end
101
+
102
+ # Deletes a domain registration.
103
+ #
104
+ # The domain registration must be less than 5 days old. eNom requires an +EndUserIP+
105
+ # to be sent with the request, this is set to +127.0.0.1+.
106
+ #
107
+ # @param [String] domain Name of the registered domain
108
+ # @return [true] if successfully deleted
109
+ # @return [Hash] Error details with +:string+, +:source+ and +:section+ information
110
+ def delete_registration(domain)
111
+ xml = send_recv(:DeleteRegistration, split_domain(domain).merge(:EndUserIP => "127.000.000.001"))
112
+
113
+ return true if xml.DomainDeleted?
114
+
115
+ { :string => xml.ErrString,
116
+ :source => xml.ErrSource,
117
+ :section => xml.ErrSection }
118
+ end
119
+
120
+ # Gets the list of nameserver for a domain.
121
+ #
122
+ # @param [String] domain name of domain to collect nameservers of
123
+ # @return [Array<String>] array of nameservers
124
+ # @return [false] no nameservers for the domain
125
+ def get_dns(domain)
126
+ xml = send_recv(:GetDNS, split_domain(domain))
127
+ return false if xml.RRPCode != '200'
128
+
129
+ nameservers = []
130
+ xml.dns { nameservers << xml.strip }
131
+ nameservers
132
+ end
133
+
134
+ # Get the number of domains in the account in specific groups.
135
+ #
136
+ # The groups of domains and their result keys are:
137
+ # - (Integer) +:registered+ -- Registered
138
+ # - (Integer) +:hosted+ -- Hosted
139
+ # - (Integer) +:expiring+ -- Expiring
140
+ # - (Integer) +:expired+ -- Expired
141
+ # - (Integer) +:redemption+ -- Redemption
142
+ # - (Integer) +:extended_redemption+ -- Extended Redemption
143
+ # - (Integer) +:processing+ -- Processing
144
+ # - (Integer) +:watch_list+ -- Watch List
145
+ #
146
+ # @return [Hash] Hash of number of domains in each group
147
+ def get_domain_count
148
+ xml = send_recv(:GetDomainCount)
149
+
150
+ { :registered => xml.RegisteredCount.to_i,
151
+ :hosted => xml.HostCount.to_i,
152
+ :expiring => xml.ExpiringCount.to_i,
153
+ :expired => xml.ExpiredDomainsCount.to_i,
154
+ :redemption => xml.RGP.to_i,
155
+ :extended_redemption => xml.ExtendedRGP.to_i,
156
+ :processing => xml.ProcessCount.to_i,
157
+ :watch_list => xml.WatchlistCount.to_i }
158
+ end
159
+
160
+ # Get the expiration date of a domain
161
+ #
162
+ # @param [String] domain Domain name
163
+ # @return [Time] expiration date of the domain
164
+ def get_domain_exp(domain)
165
+ xml = send_recv(:GetDomainExp, split_domain(domain))
166
+ Time.parse(xml.ExpirationDate.strip)
167
+ end
168
+
169
+ # Get the domain information for a domain
170
+ #
171
+ # The information returned includes the following
172
+ # - (Time) +:expires+ -- Expiration date
173
+ # - (String) +:status+ -- Status
174
+ # - (Array) +:nameservers+ -- Nameserver names
175
+ #
176
+ # @param [String] domain Domain name
177
+ # @return [Hash] information for the domain
178
+ def get_domain_info(domain)
179
+ xml = send_recv(:GetDomainInfo, split_domain(domain))
180
+ xml = xml.GetDomainInfo
181
+
182
+ nameservers = []
183
+ xml.services.entry do |entry,_|
184
+ next unless entry['name'] == 'dnsserver'
185
+ entry.configuration.dns do |dns,_|
186
+ nameservers << dns.to_s
187
+ end
188
+ end
189
+
190
+ { :expires => Time.parse(xml.status.expiration.strip),
191
+ :status => xml.status.registrationstatus.strip,
192
+ :nameservers => nameservers }
193
+ end
194
+
195
+ # Get the registration status of a domain. Used for TLDs which
196
+ # do not register in real time.
197
+ #
198
+ # The hash returned includes the following information:
199
+ # - (String) +:order_id+ -- Order ID
200
+ # - (Integer) +:in_account+ -- In Account, one of 0, 1, 2
201
+ # - (String) +:description+ -- Description of In Account
202
+ # - (Time) +:expires+ -- Expiration Date
203
+ #
204
+ # The +:in_account+ field will have one of the following numeric values and meanings
205
+ # - 0: Domain is not in the eNom database
206
+ # - 1: Domain is in the eNom database and the recievers account
207
+ # - 2: Domain is in the eNom database but not the receivers account
208
+ #
209
+ # The +:description+ field contains a textual representation of the +:in_account+
210
+ # value, it will not necessarily match those given above. The meanings however
211
+ # should be correct.
212
+ #
213
+ # @overload get_domain_status(domain)
214
+ # @param [String] domain Name of the domain to get status for
215
+ # @overload get_domain_status(domain, order_id, order_type = :purchase)
216
+ # @param [String] domain Name of the domain to get status for
217
+ # @param [String] order_id Order ID to get information for
218
+ # @param [Symbol] order_type Type of order information to obtain, +:purchase+, +:transfer+, or +:extend+
219
+ # @return [Hash] Hash of registration information
220
+ def get_domain_status(domain, order_id = nil, order_type = :purchase)
221
+ order_opts = order_id.nil? ? {} : { :OrderID => order_id, :OrderType => order_type }
222
+ xml = send_recv(:GetDomainStatus, split_domain(domain).merge(order_opts))
223
+
224
+ { :orderid => xml.OrderID,
225
+ :in_account => xml.InAccount.to_i,
226
+ :description => xml.StatusDesc,
227
+ :expires => Time.parse(xml.ExpDate.strip) }
228
+ end
229
+
230
+ # Get a list of the domains in the Expired, Redemption and Extended Redemption groups.
231
+ #
232
+ # The returned hash has the following keys
233
+ # - (Array) +:expired+ -- Expired domains
234
+ # - (Array) +:redemption+ -- Domains in Redemption Grace Period (RGP)
235
+ # - (Array) +:extended_redemption+ -- Domains in Extended RGP
236
+ #
237
+ # Each array contains hashes with the following keys
238
+ # - (String) +:name+ -- The domain name
239
+ # - (String) +:id+ -- The domains eNom registry ID
240
+ # - (Time) +:date+ -- Expiration date of the domain
241
+ # - (BOOL) +:locked+ -- Domain locked status
242
+ #
243
+ # @return [Hash] Hash of expired domains information
244
+ def get_expired_domains
245
+ xml = send_recv(:GetExpiredDomains)
246
+
247
+ domains = {:expired => [], :extended_redemption => [], :redemption => []}
248
+ xml.DomainDetail do
249
+ case xml.status
250
+ when /Expired/i
251
+ domains[:expired]
252
+ when /Extended RGP/i
253
+ domains[:extended_redemption]
254
+ when /RGP/i
255
+ domains[:redemption]
256
+ end << {
257
+ :name => xml.DomainName,
258
+ :id => xml.DomainNameID,
259
+ :date => Time.parse(xml.send('expiration-date')),
260
+ :locked => xml.lockstatus =~ /Locked/i
261
+ }
262
+ end
263
+ domains
264
+ end
265
+
266
+ # @param [String] domain Domain name to renew
267
+ # @param [String] period Number of years to extend the registration by
268
+ # @return [String] Order ID of the renewal
269
+ # @return [false] the order did not succeed
270
+ def extend(domain, period = 1)
271
+ xml = send_recv(:Extend, split_domain(domain).merge(:NumYears => period.to_i))
272
+
273
+ return false if xml.RRPCode != '200'
274
+ xml.OrderID
275
+ end
276
+
277
+ # Get the list of extended attributes required by a TLD
278
+ #
279
+ # The returned array of extended attributes contains hashes of the attribute details.
280
+ # The details include the following information
281
+ # - (String) +:id+ -- eNom internal attribute ID
282
+ # - (String) +:name+ -- Form parameter name
283
+ # - (String) +:title+ -- Short definition of the parameter value
284
+ # - (BOOL) +:application+ -- Attribute required for Registrant contact
285
+ # - (BOOL) +:user_defined+ -- Attribute value must be provided by user
286
+ # - (BOOL) +:required+ -- Attribute is required
287
+ # - (String) +:description+ -- Long definition of the parameter value
288
+ # - (String) +:is_child+ -- Is a child of another
289
+ # - (Array) +:options+ -- Array of options for the attribute
290
+ #
291
+ # Attribute options include the following information
292
+ # - (String) +:id+ -- eNom internal attribute option ID
293
+ # - (String) +:value+ -- Value of the option
294
+ # - (String) +:title+ -- Short definition of the parameter value
295
+ # - (String) +:description+ -- Long definition of the parameter value
296
+ #
297
+ # @param [String] tld Top Level Domain
298
+ # @return [Array] extended attributes, their details and valid options
299
+ def get_ext_attributes(tld)
300
+ xml = send_recv(:GetExtAttributes, :TLD => tld)
301
+
302
+ attrs = []
303
+ xml.Attributes do
304
+ xml.Attribute do
305
+ h = {
306
+ :id => xml.ID,
307
+ :name => xml.Name,
308
+ :title => xml.Title,
309
+ :application => xml.Application == '2',
310
+ :user_defined => xml.UserDefined?,
311
+ :required => xml.Required?,
312
+ :description => xml.Description,
313
+ :is_child => xml.IsChild?,
314
+ :options => Array.new }
315
+ attrs << h
316
+ xml.Options do
317
+ xml.Option do
318
+ h[:options] << {
319
+ :id => xml.ID,
320
+ :value => xml.Value,
321
+ :title => xml.Title,
322
+ :description => xml.Description
323
+ }
324
+ end
325
+ end
326
+ end
327
+ end
328
+ attrs
329
+ end
330
+
331
+ # Get renewal information for a domain
332
+ #
333
+ # The returned hash contains the following keys
334
+ # - (Time) +:expiration+ -- Time the domain expires
335
+ # - (Integer) +:max_extension+ -- Maximum number of years which can be added
336
+ # - (Integer) +:min_extension+ -- Minimum number of years which can be added
337
+ # - (BOOL) +:registrar_hold+ -- Registrar hold state
338
+ # - (Float) +:balance+ -- Current account balance
339
+ # - (Float) +:available+ -- Available account balance
340
+ #
341
+ # @param [String] domain Domain name
342
+ # @return [Hash] Renewal information
343
+ def get_extend_info(domain)
344
+ xml = send_recv(:GetExtendInfo, split_domain(domain))
345
+
346
+ { :expiration => Time.parse(xml.Expiration),
347
+ :max_extension => xml.MaxExtension.to_i,
348
+ :min_extension => xml.MinAllowed.to_i,
349
+ :registrar_hold => xml.RegistrarHold?,
350
+ :balance => xml.Balance.to_f,
351
+ :available => xml.AvailableBalance.to_f }
352
+ end
353
+
354
+ # Get detailed information about an order
355
+ #
356
+ # The returned hash contains the following keys
357
+ # - (Boolean) +:result+ -- Order exists
358
+ # - (Float) +:amount+ -- Billed amount
359
+ # - (Array) +:details+ -- Details
360
+ # - (String) +:status+ -- Order Status
361
+ #
362
+ # The +:details+ result key array contains hashes with the following keys
363
+ # - (String) +:product_type+ -- Order detail item type
364
+ # - (String) +:description+ -- Description of the detail
365
+ # - (String) +:status+ -- Status of the order detail
366
+ # - (Integer) +:quantity+ -- Number of the details of this type
367
+ # - (Float) +:amount+ -- Amount paid for detail
368
+ #
369
+ # @param [String] order_id ID of the order
370
+ # @return [Hash] order information
371
+ def get_order_detail(order_id)
372
+ xml = send_recv(:GetOrderDetail, :OrderID => order_id)
373
+
374
+ info = {}
375
+ xml.Order do
376
+ info[:result] = xml.Result?
377
+ info[:amount] = xml.OrderBillAmount
378
+ info[:status] = xml.OrderStatus # If this doesn't exist, then its under OrderDetail
379
+ info[:details] = []
380
+
381
+ xml.OrderDetail do
382
+ info[:details] << {
383
+ :product_type => xml.ProductType,
384
+ :description => xml.Description,
385
+ :status => xml.Status,
386
+ :quantity => xml.Quantity.to_i,
387
+ :amount => xml.AmountPaid
388
+ }
389
+ end
390
+ end
391
+ end
392
+
393
+ # Get list of the account orders
394
+ #
395
+ # The returned array contains hashes with the following keys
396
+ # - (String) +:id+ -- Order ID number
397
+ # - (Time) +:date+ -- Date the order was placed
398
+ # - (String) +:status+ -- Status of the order
399
+ # - (BOOL) +:processed+ -- Whether the order has been processed
400
+ #
401
+ # @param [Hash] options Options to get the order list with
402
+ # @option options [Integer] :start Starting offset in order list
403
+ # @option options [String, #strftime] :begin String date or Date of earliest order to retrieve.
404
+ # If omitted then 6 months of orders are retrieved
405
+ # @option options [String, #strftime] :end String date or Date or lastest order to retrieve.
406
+ # If omitted then the end is today
407
+ # @return [Array] orders of :id, :date, :status and :processed
408
+ def get_order_list(options = {})
409
+ xml = send_recv(:GetOrderList, :Start => (options[:start] || 1)) do |h|
410
+ h[:BeginDate] = if options[:begin].respond_to?(:strftime)
411
+ options[:begin].strftime("%m/%d/%Y")
412
+ else
413
+ options[:begin]
414
+ end
415
+
416
+ h[:EndDate] = if options[:end].respond_to?(:strftime)
417
+ options[:end].strftime("%m/%d/%Y")
418
+ else
419
+ options[:end]
420
+ end
421
+ end
422
+
423
+ out = []
424
+ xml.OrderList do
425
+ xml.OrderDetail do
426
+ { :id => xml.OrderID,
427
+ :date => Time.parse(xml.OrderDate),
428
+ :status => xml.StatusDesc,
429
+ :processed => xml.OrderProcessFlag? }
430
+ end
431
+ end
432
+ out
433
+ end
434
+
435
+ # Get the registration status of a domain.
436
+ #
437
+ # The returned hash has the following keys
438
+ # - (BOOL) +:hold+ -- Registrar hold set
439
+ # - (String) +:registration+ -- Registration status of the domain
440
+ # - (String) +:purchase+ -- Purchase status of the domain
441
+ #
442
+ # Registration status will be one of
443
+ # 1. Processing
444
+ # 2. Registered
445
+ # 3. Hosted
446
+ # 4. Null
447
+ #
448
+ # Purchase status will be one of
449
+ # 1. Processing
450
+ # 2. Paid
451
+ # 3. Null
452
+ #
453
+ # @param [String] domain Domain name to get registration status of
454
+ # @return [Hash] :hold and :status
455
+ def get_registration_status(domain)
456
+ xml = send_recv(:GetRegistrationStatus, split_domain(domain))
457
+ { :hold => xml.RegistrarHold?, :registration => xml.RegistrationStatus, :purchase => xml.PurchaseStatus }
458
+ end
459
+
460
+ # Get the registrar lock setting for a domain
461
+ #
462
+ # @param [String] domain Domain name to check registrar lock of
463
+ # @return [Boolean] locked state, true = locked
464
+ def get_reg_lock(domain)
465
+ xml = send_recv(:GetRegLock, split_domain(domain))
466
+ xml.RegLock?
467
+ end
468
+
469
+ # Get the list of TLDs available for the account
470
+ #
471
+ # @return [Array<String>] array of TLDs available to the account
472
+ def get_tld_list
473
+ xml = send_recv(:GetTLDList)
474
+
475
+ tlds = []
476
+ xml.tldlist.tld do |tld,_|
477
+ tld.tld do
478
+ tlds << tld.to_s unless tld.to_s == ''
479
+ end
480
+ end
481
+ tlds
482
+ end
483
+
484
+ # Gets the WHOIS contact details for a domain
485
+ #
486
+ # The returned hash has the following keys
487
+ # - (Hash<Symbol,Registrant>) +:contacts+ -- Registrant object
488
+ # - (Time) +:updated_date+ -- Domain last update time
489
+ # - (Time) +:created_date+ -- Domain creation date
490
+ # - (Time) +:expiry_date+ -- Domain expiration date
491
+ # - (Array) +:nameservers+ -- Array of name server names
492
+ # - (String) +:status+ -- Registrar lock status
493
+ #
494
+ # @param [String] domain Domain name to retrieve WHOIS information for
495
+ # @return [Hash] response data
496
+ def get_whois_contact(domain)
497
+ xml = send_recv(:GetWhoisContact, split_domain(domain))
498
+
499
+ out = {:contacts => {}}
500
+ xml.GetWhoisContacts do
501
+ xml.contacts.contact do |contact,_|
502
+ case contact['ContactType']
503
+ when 'Registrant'
504
+ out[:contacts][:registrant] = Registrant.from_xml(contact)
505
+ when 'Administrative'
506
+ out[:contacts][:administrative] = Registrant.from_xml(contact)
507
+ when 'Technical'
508
+ out[:contacts][:technical] = Registrant.from_xml(contact)
509
+ when 'Billing'
510
+ out[:contacts][:billing] = Registrant.from_xml(contact)
511
+ end
512
+ end
513
+
514
+ xml.send("rrp-info") do
515
+ out[:updated_date] = Time.parse(xml.send("updated-date"))
516
+ out[:created_date] = Time.parse(xml.send("created-date"))
517
+ out[:expiry_date] = Time.parse(xml.send("registration-expiration-date"))
518
+ out[:nameservers] = Array.new
519
+ xml.nameserver do
520
+ xml.nameserver do
521
+ out[:nameservers] << xml.to_s.downcase
522
+ end
523
+ end
524
+ xml.status do
525
+ out[:status] = xml.status # != registration status from get_domain_info. = lock status
526
+ end
527
+ end
528
+ end
529
+
530
+ out
531
+ end
532
+
533
+ def get_contacts(domain)
534
+ xml = send_recv(:GetContacts, split_domain(domain))
535
+ xml = xml.GetContacts
536
+
537
+ out = {}
538
+ out[:registrant] = Registrant.from_xml(xml.Registrant)
539
+ out[:aux_billing] = Registrant.from_xml(xml.AuxBilling)
540
+ out[:administrative] = Registrant.from_xml(xml.Admin)
541
+ out[:technical] = Registrant.from_xml(xml.Tech)
542
+ out[:billing] = Registrant.from_xml(xml.Billing)
543
+ out
544
+ end
545
+
546
+ # Push a domain to another eNom account.
547
+ #
548
+ # This is much like a domain transfer except it is wholly within the scope
549
+ # of eNoms registration system.
550
+ #
551
+ # @param [String] domain Domain name to push
552
+ # @param [String] account_id eNom Account ID to push the domain to
553
+ # @param [Boolean] push_contact Should push the domain contact information
554
+ # @return [Boolean] whether the push was successful or not
555
+ def push_domain(domain, account_id, push_contact = true)
556
+ xml = send_recv(:PushDomain, split_domain(domain).merge(
557
+ :AccountID => account_id, :PushContact => (push_contact ? 1 : 0)))
558
+ xml.PushDomain?
559
+ end
560
+
561
+ # Register a domain name server
562
+ #
563
+ # @param [String] nameserver Nameserver to register
564
+ # @param [String] ip IPv4 Address of the nameserver
565
+ # @return true if registration was successful
566
+ # @raise [ResponseError] if an error occurred
567
+ def register_nameserver(nameserver, ip)
568
+ send_recv(:RegisterNameServer, :Add => 'true', :NSName => nameserver, :IP => ip)
569
+ true # send_recv will raise a ResponseError if ErrCount > 0
570
+ end
571
+
572
+ # Sets the registrar lock on a domain name.
573
+ #
574
+ # @param [String] domain Domain to set the lock on
575
+ # @param [Boolean] new_state True to lock, False to unlock
576
+ # @return [false] if setting failed
577
+ # @return [String] lock status
578
+ def set_reg_lock(domain, new_state) # true to lock, false to unlock
579
+ xml = send_recv(:SetRegLock, split_domain(domain).merge(:UnlockRegistrar => (new_state ? '0' : '1')))
580
+
581
+ ret = xml.RegistrarLock.strip
582
+ return false if ret == 'Failed'
583
+ ret
584
+ end
585
+
586
+ # Reactivates an expired domain in real time.
587
+ #
588
+ # @param [String] domain Expired Domain name to register
589
+ # @param [Number] years Number of years to register the domain for
590
+ # @return [String] response status
591
+ def update_expired_domains(domain, years) # Like :extend, but for expired domains
592
+ xml = send_recv(:UpdateExpiredDomains, :DomainName => domain, :NumYears => years.to_i)
593
+ xml.Status.strip
594
+ end
595
+
596
+ # Change the IP address of a registered name server.
597
+ #
598
+ # @param [String] nameserver Nameserver to update the IP address of
599
+ # @param [String] old_ip Old IPv4 address of the nameserver
600
+ # @param [String] new_ip New IPv4 address of the nameserver
601
+ # @return [Boolean] success or failure of the update
602
+ def update_nameserver(nameserver, old_ip, new_ip)
603
+ xml = send_recv(:RegisterNameServer, :NS => nameserver, :OldIP => old_ip, :NewIP => new_ip)
604
+ xml.NSSuccess?
605
+ end
606
+
607
+ # Modify the name servers for a domain.
608
+ #
609
+ # @param [String] domain Domain name to set the nameserver of
610
+ # @param [String, ...] nameservers Nameservers to set for the domain
611
+ # @raise [RuntimeError] if number of nameservers exceeds 12
612
+ def modify_ns(domain, *nameservers)
613
+ raise "Maximum nameserver limit is 12" if nameservers.size > 12
614
+ xml = send_recv(:ModifyNS, split_domain(domain)) do |d|
615
+ if nameservers.empty?
616
+ d['NS1'] = ''
617
+ else
618
+ nameservers.each_with_index do |n,i|
619
+ d["NS#{i}"] = n
620
+ end
621
+ end
622
+ end
623
+ xml.RRPCode == '200'
624
+ end
625
+
626
+ # Gets information about a domain
627
+ #
628
+ # The returned hash contains the following keys
629
+ # - (BOOL) +:known+ -- Whether the domain is known to eNom
630
+ # - (BOOL) +:in_account+ -- Whether the domain belongs to the account
631
+ # - (String) +:last_order_id+ -- Last Order ID for the domain if domain belongs to the account
632
+ #
633
+ # @param [String] domain Domain to check the status of
634
+ # @param [String] type Order Type to query with, one of 'Purchase', 'Transfer' or 'Extend'
635
+ # @return [Hash] of information about the domain
636
+ def status_domain(domain, type = 'Purchase')
637
+ raise ArgumentError, "type must be Purchase, Transfer or Extend" unless %w(purchase transfer extend).include?(type.downcase)
638
+ begin
639
+ xml = send_recv(:StatusDomain, split_domain(domain).merge(:OrderType => type))
640
+
641
+ xml.DomainStatus do
642
+ return { :known => (xml.Known == 'Known'),
643
+ :in_account => xml.InAccount?, # It'll be either 0 or 1 here, case 2 raises an exception
644
+ :last_order_id => xml.OrderID }
645
+ end
646
+ rescue ResponseError => e
647
+ if e.messages.include?("The domain does not belong to this account")
648
+ # It returns an error if the domain is known to eNom but in another account
649
+ return { :known => true, :in_account => false }
650
+ end
651
+ end
652
+ end
653
+
654
+ # Purchase a domain name.
655
+ #
656
+ # The returned hash has the following keys
657
+ # - (Symbol) +:result+ -- Either +:registered+ or +:ordered+. The latter if the TLD does not support real time registrations.
658
+ # - (String) +:order_id+ -- Order ID of the purchase
659
+ #
660
+ # @param [String] domain Domain name to register
661
+ # @param [Registrant] registrant Registrant of the domain
662
+ # @param [Array<String>, nil] nameservers Nameservers to set for the domain, nil sends blank NS1
663
+ # @param [Hash] options Options to configure the registration
664
+ # @option options [Integer] :period Number of years to register the domain for
665
+ # @return [Hash] :result => :registered and :order_id if successful
666
+ # @return [Hash] :result => :ordered and :order_id if not a Real Time TLD
667
+ # @raise [RuntimeError] if more than 12 nameservers are passed
668
+ # @raise [ResponseError] if regisration failed
669
+ def purchase(domain, registrant, nameservers, options = {})
670
+ raise "Maximum nameserver limit is 12" if nameservers.size > 12
671
+ opts = registrant.to_post_data('Registrant')
672
+ opts[:NumYears] = options.delete(:period) if options.has_key?(:period)
673
+
674
+ xml = send_recv(:Purchase, split_domain(domain).merge(opts)) do |d|
675
+ if nameservers.empty?
676
+ d['NS1'] = ''
677
+ else
678
+ nameservers.each_with_index do |n,i|
679
+ d["NS#{i}"] = n
680
+ end
681
+ end
682
+ end
683
+
684
+ case xml.RRPCode.to_i
685
+ when 200
686
+ return { :result => :registered, :order_id => xml.OrderID }
687
+ when 1300
688
+ raise ResponseError.new([xml.RRPText]) if xml.IsRealTimeTLD?
689
+ return { :result => :ordered, :order_id => xml.OrderID }
690
+ end
691
+ end
692
+
693
+ private
694
+ # Split the domain into Top level and Domain components
695
+ #
696
+ # @param [String] domain Domain name to split
697
+ # @return [Hash] with :SLD and :TLD parts
698
+ def split_domain(domain)
699
+ s, t = domain.split('.', 2)
700
+ {:SLD => s, :TLD => t}
701
+ end
702
+
703
+ # Sends the payload
704
+ #
705
+ # @param [String] method API command name
706
+ # @param [Hash] post_data Hash of POST data
707
+ # @yield [post_data] Block to append additional post data
708
+ # @yieldparam [Hash] post_data Hash of POST data
709
+ # @raise [ResponseError] if any errors are returned from the command
710
+ # @raise [IncompleteResponseError] if the response does not indicate Done
711
+ def send_recv(method, post_data = {}, &block)
712
+ yield post_data if block
713
+ @response = @conn.send(method, post_data)
714
+ xml = Nokogiri::XML.parse(@response)
715
+
716
+ if (err_count = xml.xpath('//ErrCount').first.content.strip.to_i) > 0
717
+ errs = (1..err_count).map { |i| xml.xpath("//Err#{i}").first.content.strip }
718
+ raise ResponseError.new(errs)
719
+ end
720
+
721
+ unless xml.xpath('//Done').first.content.strip =~ /true/i
722
+ raise IncompleteResponseError.new(xml)
723
+ end
724
+
725
+ demolisher = Demolisher.demolish(xml)
726
+ demolisher.send("interface-response")
727
+ end
728
+ end
729
+ end
@@ -0,0 +1,66 @@
1
+ require 'net/https'
2
+
3
+ module EnomAPI
4
+ # Interface proxy for the eNom Reseller API
5
+ class Interface
6
+ # Version of the Interface class, sent with the HTTP requests
7
+ VERSION = '0.1.0'
8
+
9
+ # @param [String] user eNom Account Login ID
10
+ # @param [String] passwd eNom Account Password
11
+ # @param [String] server Server to connect to
12
+ def initialize(user, passwd, server = 'reseller.enom.com')
13
+ @user, @passwd = user, passwd
14
+ @uri = URI.parse('https://%s/interface.asp' % server)
15
+ end
16
+
17
+ # @yield [q] Search query block
18
+ # @yieldparam [SearchQuery] q SearchQuery instance
19
+ # @return [String] XML Body of the Search Query results
20
+ def search
21
+ q = yield SearchQuery.new
22
+ send_request(q.to_post_data)
23
+ end
24
+
25
+ # @param [Symbol] meth API command to execute
26
+ # @param [Hash] options POST data to send to the API
27
+ # @return [String] XML Body of the response
28
+ def method_missing(meth, options = {})
29
+ send_request(options.merge(:command => meth.to_s, :responseType => 'xml'))
30
+ end
31
+ private
32
+ # @param [Hash] data POST data to send to interface.asp
33
+ # @param [Integer] attempts Number of attempts to try, default 3
34
+ # @return [String] XML Body of the response
35
+ def send_request(data, attempts = 3)
36
+ begin
37
+ s_client = Net::HTTP.new(@uri.host, @uri.port)
38
+ s_client.use_ssl = true
39
+ s_client.verify_mode = OpenSSL::SSL::VERIFY_NONE
40
+
41
+ @response = s_client.start do |https|
42
+ @request = Net::HTTP::Post.new(@uri.path)
43
+ @request.add_field('User-Agent', "Ruby eNom API Client v#{VERSION}")
44
+ @request.content_type = 'application/x-www-form-urlencoded'
45
+
46
+ @request.body = data.merge(:uid => @user, :pw => @passwd).map { |k,v|
47
+ "#{@request.send(:urlencode, k.to_s)}=#{@request.send(:urlencode, v.to_s)}" }.join("&")
48
+
49
+ https.request(@request)
50
+ end
51
+
52
+ if @response.kind_of?(Net::HTTPSuccess)
53
+ @response.body
54
+ else
55
+ raise @response
56
+ end
57
+ rescue ::Timeout::Error => e
58
+ if attempts == 1
59
+ raise e
60
+ else
61
+ send_request(data, attempts - 1)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,91 @@
1
+ require 'demolisher'
2
+
3
+ module EnomAPI
4
+ # Represents a registrant or other type of Contact in the eNom API
5
+ class Registrant
6
+ # @param [Demolisher, String] xmldoc Demolisher or XML String of registrant information
7
+ # @return [Registrant] Registrant composed from the information in the xmldoc
8
+ def self.from_xml(xmldoc)
9
+ @xml = xml = xmldoc.kind_of?(Demolisher::Node) ? xmldoc : Demolisher.demolish(xmldoc.to_s)
10
+ r = new("", "")
11
+
12
+ mapping = if xml.FName
13
+ from_xml_mapping_one
14
+ elsif xml.RegistrantFirstName
15
+ from_xml_mapping_two('Registrant')
16
+ elsif xml.AuxBillingFirstName
17
+ from_xml_mapping_two('AuxBilling')
18
+ elsif xml.TechFirstName
19
+ from_xml_mapping_two('Tech')
20
+ elsif xml.AdminFirstName
21
+ from_xml_mapping_two('Admin')
22
+ elsif xml.BillingFirstName
23
+ from_xml_mapping_two('Billing')
24
+ end
25
+
26
+ mapping.each do |meth, el|
27
+ case el
28
+ when Array
29
+ r.send(:"#{meth}=", el.map { |n| xml.send(n).to_s }.delete_if { |n| n.nil? || n == "" }.join("\n"))
30
+ else
31
+ r.send(:"#{meth}=", xml.send(el).to_s)
32
+ end
33
+ end
34
+
35
+ r
36
+ end
37
+
38
+ attr_accessor :id, :firstname, :lastname, :phone, :phone_extension, :fax, :email,
39
+ :organisation, :job_title, :address, :city, :state, :postal_code, :country
40
+
41
+ # @param [String] first Registrant first name
42
+ # @param [String] last Registrant last name
43
+ # @yield block to configure the registrant, any method on Registrant may be called.
44
+ def initialize(first, last, &blk)
45
+ raise ArgumentError, "first and last may not be nil" unless first && last
46
+
47
+ @firstname, @lastname = first, last
48
+ instance_eval(&blk) if blk
49
+ end
50
+
51
+ # Converts the object into a form suitable for POSTing to the eNom API
52
+ #
53
+ # @param [String] prefix String to prepend to key names
54
+ # @return [Hash] Hash of data for the registrant
55
+ def to_post_data(prefix=nil)
56
+ data = {
57
+ "#{prefix}FirstName" => firstname,
58
+ "#{prefix}LastName" => lastname,
59
+ "#{prefix}City" => city,
60
+ "#{prefix}StateProvince" => state,
61
+ "#{prefix}PostalCode" => postal_code,
62
+ "#{prefix}Country" => country,
63
+ "#{prefix}EmailAddress" => email,
64
+ "#{prefix}Phone" => phone,
65
+ "#{prefix}Fax" => fax }
66
+ data["#{prefix}Address1"], data["#{prefix}Address2"] = address.split("\n", 2)
67
+
68
+ unless organisation.nil? || organisation == ''
69
+ data["#{prefix}OrganizationName"] = organisation
70
+ data["#{prefix}JobTitle"] = (job_title || "Domains Manager")
71
+ end
72
+ data
73
+ end
74
+ private
75
+ def self.from_xml_mapping_one
76
+ { :firstname => :FName, :lastname => :LName,
77
+ :organisation => :Organization, :job_title => :JobTitle,
78
+ :city => :City, :state => :StateProvince, :postal_code => :PostalCode, :country => :Country,
79
+ :phone => :Phone, :phone_extension => :PhoneExt, :fax => :Fax, :email => :EmailAddress,
80
+ :address => [:Address1, :Address2] }
81
+ end
82
+ def self.from_xml_mapping_two(p = nil)
83
+ { :id => "#{p}PartyID",
84
+ :firstname => "#{p}FirstName", :lastname => "#{p}LastName",
85
+ :organisation => "#{p}OrganizationName", :job_title => "#{p}JobTitle",
86
+ :city => "#{p}City", :state => "#{p}StateProvince", :postal_code => "#{p}PostalCode", :country => "#{p}Country",
87
+ :phone => "#{p}Phone", :phone_extension => "#{p}PhoneExt", :fax => "#{p}Fax", :email => "#{p}EmailAddress",
88
+ :address => ["#{p}Address1", "#{p}Address2"] }
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,51 @@
1
+ module EnomAPI
2
+ # Class to define the parameters of an eNom search query
3
+ class SearchQuery
4
+ def initialize
5
+ @options = {'responsetype' => 'xml', 'command' => 'advanceddomainsearch'}
6
+ end
7
+
8
+ # Change the ordering of the query results.
9
+ #
10
+ # @param [String] opt Ordering option, one of sld, tld, nsstatus, expdate, or renew
11
+ def order_by(opt)
12
+ raise ArgumentError, "invalid order by value" unless %w(sld tld nsstatus expdate renew).include?(opt)
13
+ @options['orderby'] = opt
14
+ self
15
+ end
16
+
17
+ # Limit the quantity of results returned
18
+ #
19
+ # @overload limit(number)
20
+ # @param [Integer] number Number of records to return
21
+ # @overload limit(start, number)
22
+ # @param [Integer] start Start position in the results
23
+ # @param [Integer] number Number of records to return
24
+ def limit(num_or_start, num = nil)
25
+ if num.nil?
26
+ raise ArgumentError, "invalid limit" unless num_or_start.kind_of?(Integer)
27
+ @options['recordstoreturn'] = num_or_start
28
+ else
29
+ raise ArgumentError, "invalid limit start" unless num_or_start.kind_of?(Integer)
30
+ raise ArgumentError, "invalid limit size" unless num_or_start.kind_of?(Integer)
31
+ @options['recordstoreturn'] = num
32
+ @options['startposition'] = num_or_start
33
+ end
34
+ self
35
+ end
36
+
37
+ # @param [Hash] conditions Search conditions
38
+ def where(conditions = {})
39
+ @options = conditions.merge(@options)
40
+ self
41
+ end
42
+
43
+ # @return POST data options
44
+ def to_post_data
45
+ if @options[:creationdate].respond_to?(:strftime)
46
+ @options[:creationdate] = @options[:creationdate].strftime("%m/%d/%Y")
47
+ end
48
+ @options
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'enom-api'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestEnomApi < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: enom-api
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Geoff Garside
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-29 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thoughtbot-shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: yard
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: demolisher
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 7
58
+ segments:
59
+ - 0
60
+ - 6
61
+ - 0
62
+ version: 0.6.0
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ description: Client for communicating with the eNom API
66
+ email: geoff@geoffgarside.co.uk
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - LICENSE
73
+ - README.rdoc
74
+ files:
75
+ - .document
76
+ - LICENSE
77
+ - README.rdoc
78
+ - Rakefile
79
+ - VERSION
80
+ - enom-api.gemspec
81
+ - lib/enom-api.rb
82
+ - lib/enom-api/client.rb
83
+ - lib/enom-api/interface.rb
84
+ - lib/enom-api/registrant.rb
85
+ - lib/enom-api/search_query.rb
86
+ - test/helper.rb
87
+ - test/test_enom-api.rb
88
+ has_rdoc: true
89
+ homepage: http://github.com/geoffgarside/enom-api
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options: []
94
+
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: 3
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ requirements: []
116
+
117
+ rubyforge_project:
118
+ rubygems_version: 1.6.2
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: eNom API Client
122
+ test_files:
123
+ - test/helper.rb
124
+ - test/test_enom-api.rb