reg.api2 0.0.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ --require spec_helper.rb
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ notifications:
2
+ recipients:
3
+ - akzhan.abdulin@gmail.com
4
+ rvm:
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - 2.0.0
data/.yardopts ADDED
@@ -0,0 +1,7 @@
1
+ --markup markdown
2
+ --markup-provider redcarpet
3
+ --charset utf-8
4
+ --readme README.md
5
+ -
6
+ README.md
7
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in reg.api2.gemspec
4
+ gemspec
5
+
6
+ gem "coveralls", :require => false, :platforms => [:mri_19, :mri_20], :group => :development
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Akzhan Abdulin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # REG.API 2 for Ruby
2
+
3
+ [![build status](https://secure.travis-ci.org/regru/reg_api2-ruby.png)](https://travis-ci.org/regru/reg_api2-ruby)
4
+ [![Code Climate](https://codeclimate.com/github/regru/reg_api2-ruby.png)](https://codeclimate.com/github/regru/reg_api2-ruby)
5
+ [![Coverage Status](https://coveralls.io/repos/regru/reg_api2-ruby/badge.png?branch=master)](https://coveralls.io/r/regru/reg_api2-ruby)
6
+
7
+ REG.API v2 Implementation.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'reg.api2'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install reg.api2
22
+
23
+ ## Usage
24
+
25
+ ### List of services by specified identifiers
26
+
27
+ ```ruby
28
+ require "reg_api2"
29
+
30
+ RegApi2.service.nop(services: [
31
+ { dname:"test.ru" },
32
+ { dname: "test.su", servtype: "srv_hosting_ispmgr" },
33
+ { service_id: 111111 },
34
+ { service_id: "22bug22" },
35
+ { surprise: "surprise.ru" }
36
+ ])
37
+ ```
38
+
39
+ ## Documentation
40
+
41
+ Simply do
42
+
43
+ ```bash
44
+ bundle exec rake yard
45
+ open doc/index.html
46
+ ```
47
+
48
+ Also documentation available at https://www.reg.com/support/help/API-version2
49
+
50
+ ## Contributing
51
+
52
+ 1. Fork it
53
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
54
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
55
+ 4. Push to the branch (`git push origin my-new-feature`)
56
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require "bundler/gem_tasks"
4
+
5
+ APP_ROOT = File.dirname(__FILE__).freeze
6
+
7
+ begin
8
+ require 'rspec/core/rake_task'
9
+
10
+ RSpec::Core::RakeTask.new
11
+
12
+ RSpec::Core::RakeTask.new(:rcov) do |t|
13
+ t.rcov = true
14
+ t.ruby_opts = '-w'
15
+ t.rcov_opts = %q[-Ilib --exclude "spec/*,gems/*"]
16
+ end
17
+ rescue LoadError
18
+ $stderr.puts "RSpec not available. Install it with: gem install rspec-core rspec-expectations"
19
+ end
20
+
21
+ task :default => :spec
22
+
23
+ begin
24
+ require 'yard'
25
+
26
+ YARD::Rake::YardocTask.new do |yard|
27
+ version = File.exists?('VERSION') ? IO.read('VERSION') : ""
28
+ yard.options << "--title='reg.api2 #{version}'"
29
+ end
30
+ rescue LoadError
31
+ $stderr.puts "Please install YARD with: gem install yard"
32
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module RegApi2
3
+ # Internal DSL Builder. Provides metamethods.
4
+ module Builder
5
+
6
+ # Extends module by metamethods `category` and `define`.
7
+ def self.included(mod)
8
+ mod.module_eval do
9
+
10
+ class << self
11
+ # @!method Sets method category
12
+ # @param category [String or NilClass] Category of methods
13
+ # @see define
14
+ def category category
15
+ @cat = category
16
+ end
17
+
18
+ # @!method Defines API method.
19
+ # @param name Name of specified method.
20
+ def define name, defopts = {}
21
+ define_method name do |opts = {}|
22
+ RegApi2.make_action(@cat, name, defopts, opts)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module RegApi2
3
+ # REG.API clients category
4
+ module Clients
5
+
6
+ include RegApi2::Builder
7
+
8
+ category :clients
9
+ define :nop
10
+
11
+ extend self
12
+ end
13
+ end
14
+
@@ -0,0 +1,46 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'reg_api2/result_contract/single_field'
4
+
5
+ module RegApi2
6
+
7
+ # REG.API common category
8
+ module Common
9
+
10
+ include RegApi2::Builder
11
+
12
+ category nil
13
+
14
+ # @!method nop
15
+ # @param None
16
+ # For testing purposes (do nothing + get the login and identifier of the user logged into the system).
17
+ # @return [Hash("login", "user_id")]
18
+ define :nop
19
+
20
+ # @!method reseller_nop
21
+ # @param None
22
+ # Similar to the nop function, except for the following aspects.
23
+ # @note Accessibility: partners
24
+ # @note Access mode: Secure HTTPS only
25
+ # @note Support of service lists: no
26
+ # @return [Hash("login", "user_id")]
27
+ define :reseller_nop
28
+
29
+
30
+ # @!method get_user_id
31
+ # @param None
32
+ # For testing purposes (returns the identifier of the user logged into the system).
33
+ # @return [String] user_id
34
+ define :get_user_id, result: :single_field, field: 'user_id'
35
+
36
+ # @!method get_service_id(opts = {})
37
+ # @param [Hash] opts The options.
38
+ # @option opts [FixNum] :service_id Service identifier.
39
+ # Gets service/domain identifier
40
+ # @return [String] service_id
41
+ define :get_service_id, result: :single_field, field: 'service_id'
42
+
43
+ extend self
44
+ end
45
+ end
46
+
@@ -0,0 +1,13 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module RegApi2
3
+ # REG.API domains category
4
+ module Domains
5
+
6
+ include RegApi2::Builder
7
+
8
+ category :domains
9
+ define :nop
10
+
11
+ extend self
12
+ end
13
+ end
@@ -0,0 +1,57 @@
1
+ require 'yajl'
2
+
3
+ module RegApi2
4
+ # Optional entities that can be used instead of clean hashes.
5
+ module Entity
6
+ # Base entity class.
7
+ class EntityBase
8
+
9
+ # Skipped property names.
10
+ SKIPPED_MEMBERS = [
11
+ "taguri" # from YAML mixin
12
+ ].freeze
13
+
14
+ # Gets instance property names
15
+ # @return [Array(String)]
16
+ def property_names
17
+ methods = self.class.public_instance_methods(false).map(&:to_s)
18
+ methods.select do |n|
19
+ true &&
20
+ !SKIPPED_MEMBERS.detect { |n3| n3 == n } &&
21
+ n =~ /^[^=\?!]+$/ &&
22
+ methods.detect { |n2| "#{n}=" == n2 } &&
23
+ true
24
+ end
25
+ end
26
+
27
+ # All r/w properties interpreted as symbol hash.
28
+ # @return [Hash] properties as hash.
29
+ def to_hash
30
+ h = {}
31
+ property_names.each do |n|
32
+ v = self.send n.to_sym
33
+ h[n.to_sym] = v unless v.nil?
34
+ end
35
+ h
36
+ end
37
+
38
+ # Returns JSON
39
+ # @return [String] JSON
40
+ # @see #to_hash
41
+ def to_json
42
+ Yajl::Encoder.encode to_hash
43
+ end
44
+
45
+ # Initializes the instance.
46
+ # opts values are assigned to properties if exist.
47
+ # @param [Hash] opts
48
+ def initialize opts = {}
49
+ methods = self.class.public_instance_methods(false).map(&:to_s)
50
+ opts.keys.each do |key|
51
+ next unless methods.detect { |m| m == "#{key}=" }
52
+ send("#{key}=", opts[key])
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,36 @@
1
+ require 'reg_api2/entity/entity_base'
2
+
3
+ module RegApi2
4
+ module Entity
5
+ # Represents REG.API user.
6
+ class User < EntityBase
7
+ # @note Required property.
8
+ attr_accessor :user_login
9
+ # @note Required property.
10
+ attr_accessor :user_password
11
+ # @note Required property.
12
+ attr_accessor :user_email
13
+ # @note Required property.
14
+ attr_accessor :user_country_code
15
+
16
+ attr_accessor :user_first_name
17
+ attr_accessor :user_last_name
18
+ attr_accessor :user_company
19
+ attr_accessor :user_jabber_id
20
+ attr_accessor :user_icq
21
+ attr_accessor :user_phone
22
+ attr_accessor :user_fax
23
+ attr_accessor :user_addr
24
+ attr_accessor :user_city
25
+ attr_accessor :user_state
26
+ attr_accessor :user_postcode
27
+ attr_accessor :user_wmid
28
+ attr_accessor :user_website
29
+
30
+ attr_accessor :user_subsribe
31
+ attr_accessor :user_mailnotify
32
+ attr_accessor :set_me_as_referrer
33
+ attr_accessor :check_only
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,175 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'yajl'
5
+
6
+ require 'reg_api2/util'
7
+
8
+ module RegApi2
9
+ # Networking Error
10
+ class NetError < Exception
11
+ end
12
+ # API Contract Error
13
+ class ContractError < Exception
14
+ end
15
+ # API Error
16
+ class ApiError < Exception
17
+ # @!attribute [r] description
18
+ # Localized error description.
19
+ attr_reader :description
20
+ # @!attribute [r] params
21
+ # Optional error params.
22
+ attr_reader :params
23
+
24
+ def initialize code, description, params
25
+ super code
26
+ @description = description
27
+ @params = params
28
+ end
29
+ end
30
+
31
+ class << self
32
+ # @!attribute [rw] username
33
+ # @return [String] User name.
34
+ attr_accessor :username
35
+ # @!attribute [rw] password
36
+ # @return [String] Password.
37
+ attr_accessor :password
38
+ # @!attribute [rw] io_encoding
39
+ # @return [String] IO encoding ('utf-8' by default).
40
+ attr_accessor :io_encoding
41
+ # @!attribute [rw] lang
42
+ # @return [String] Language ('en' by default).
43
+ attr_accessor :lang
44
+
45
+ # Default IO encoding
46
+ DEFAULT_IO_ENCODING = 'utf-8'
47
+ # Default lang.
48
+ DEFAULT_LANG = 'en'
49
+ # Default API contract for requests
50
+ DEFAULT_REQUEST_CONTRACT = RegApi2::RequestContract::Default
51
+ # Default API contract for results
52
+ DEFAULT_RESULT_CONTRACT = RegApi2::ResultContract::Default
53
+
54
+ # REG.API base URI
55
+ API_URI = URI.parse("https://api.reg.ru/api/regru2")
56
+
57
+ # Creates or gets HTTPS handler.
58
+ # @return [Net::HTTP] HTTPS handler.
59
+ def http
60
+ @http ||= begin
61
+ http = Net::HTTP.new(
62
+ API_URI.host,
63
+ API_URI.port
64
+ )
65
+ http.use_ssl = true
66
+ http
67
+ end
68
+ end
69
+
70
+ # Placeholder to inspect sent form
71
+ # @param [String] path
72
+ # @param [Hash] form
73
+ # @return Doesn't matter
74
+ def form_to_be_sent(path, form)
75
+ end
76
+
77
+ # Placeholder to inspect got response.
78
+ # @param [Net::HTTPResponse] response
79
+ # @return Doesn't matter
80
+ def got_response(response)
81
+ end
82
+
83
+ # Gets {Class} by its name.
84
+ # @param [Class] ancestor
85
+ # @param [NilClass, Class, String] name
86
+ # @param [Class] default_value
87
+ # @return [Class] {RegApi2::RequestContract}
88
+ def get_contract ancestor, name, default_value
89
+ return default_value unless name
90
+ return name if name.kind_of?(Class)
91
+ ancestor.const_get(RegApi2::Util.constantize name)
92
+ end
93
+
94
+ # Gets {RegApi2::RequestContract} by its name.
95
+ # @param [NilClass, Class, String] name
96
+ # @return [Class] {RegApi2::RequestContract}
97
+ def get_request_contract_by_name name
98
+ get_contract(RegApi2::RequestContract, name, DEFAULT_REQUEST_CONTRACT)
99
+ end
100
+
101
+ # Gets {RegApi2::ResultContract} by its name.
102
+ # @param [NilClass, Class, String] name
103
+ # @return [Class] {RegApi2::ResultContract}
104
+ def get_result_contract_by_name name
105
+ get_contract(RegApi2::ResultContract, name, DEFAULT_RESULT_CONTRACT)
106
+ end
107
+
108
+ # Gets form data for POST request
109
+ # @param [Hash] defopts
110
+ # @param [Hash] opts
111
+ # @return [Hash] Form data to be sent.
112
+ # @raise [ContractError]
113
+ def get_form_data(defopts, opts)
114
+ # HACK: REG.API doesn't know about utf-8.
115
+ io_encoding = 'utf8' if !io_encoding || io_encoding == DEFAULT_IO_ENCODING
116
+ opts = opts.to_hash if opts.respond_to?(:to_hash)
117
+ get_request_contract_by_name(defopts[:request]).new(defopts).validate(opts)
118
+ form = {
119
+ 'username' => username,
120
+ 'password' => password,
121
+ 'io_encoding' => io_encoding,
122
+ 'lang' => lang || DEFAULT_LANG,
123
+ 'output_format' => 'json',
124
+ 'input_format' => 'json',
125
+ 'show_input_params' => 0,
126
+ 'input_data' => Yajl::Encoder.encode(opts)
127
+ }
128
+ form
129
+ end
130
+
131
+ # Handles response
132
+ # @param [Hash] defopts
133
+ # @param [Net::HTTPResponse] res HTTP Response
134
+ # @return [Object] Contracted response.
135
+ # @raise [NetError]
136
+ # @raise [ApiError]
137
+ # @raise [ContractError]
138
+ def handle_response(defopts, res)
139
+ raise NetError.new(res.body) unless res.code == '200'
140
+
141
+ json = Yajl::Parser.parse(res.body)
142
+ raise ApiError.new(
143
+ json['error_code'],
144
+ json['error_text'],
145
+ json['error_params']
146
+ ) if json['result'] == 'error'
147
+
148
+ get_result_contract_by_name(defopts[:result]).new(defopts).handle_result(json)
149
+ end
150
+
151
+ # Do actual call to REG.API using POST/JSON convention.
152
+ # @param [Symbol] category
153
+ # @param [Symbol] name
154
+ # @param [Hash] defopts
155
+ # @param [Hash] opts
156
+ # @return [Hash] Result answer field.
157
+ # @raise [NetError]
158
+ # @raise [ApiError]
159
+ # @raise [ContractError]
160
+ def make_action category, name, defopts, opts = {}
161
+ req = Net::HTTP::Post.new(
162
+ category.nil? ? "#{API_URI.path}/#{name}" : "#{API_URI.path}/#{category}/#{name}"
163
+ )
164
+ form = get_form_data(defopts, opts)
165
+ form_to_be_sent(req.path, form)
166
+
167
+ req.set_form_data(form)
168
+ res = http.request(req)
169
+ got_response(res)
170
+
171
+ handle_response(defopts, res)
172
+ end
173
+
174
+ end
175
+ end
@@ -0,0 +1,66 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module RegApi2
3
+ # Contracts for API requests.
4
+ # Take a look at {RegApi2::DEFAULT_REQUEST_CONTRACT} for defaults.
5
+ module RequestContract
6
+ # Checks for specified `required` fields.
7
+ # Also checks for `optional` fields.
8
+ # Take in care :re option.
9
+ class Default
10
+ attr_reader :opts
11
+
12
+ def initialize(opts = {})
13
+ @opts = opts
14
+ end
15
+
16
+ # Normalizes `required` and `optional` fields to the form of Hash with options.
17
+ # @param [NilClass,Hash,Array, etc.] arr Something to normalize.
18
+ def to_hash arr
19
+ return {} if arr.nil?
20
+ return arr if arr.kind_of?(Hash)
21
+ arr = [ arr.to_sym ] unless arr.kind_of?(Array)
22
+ ret = {}
23
+ arr.each { |key| ret[key.to_sym] = {} }
24
+ ret
25
+ end
26
+
27
+ # Gets fields to validate
28
+ # @return [Hash] Fields to validate.
29
+ def fields_to_validate
30
+ required_fields = to_hash opts[:required]
31
+ optional_fields = to_hash opts[:optional]
32
+ required_fields.keys.each { |key| required_fields[key][:required] = true }
33
+ optional_fields.merge(required_fields)
34
+ end
35
+
36
+ # Validates specified `form` with `required` and `optional` fields.
37
+ # @param [Hash] form Form to validate.
38
+ # @raise ContractError
39
+ def validate(form)
40
+ fields = fields_to_validate
41
+ return if fields.empty?
42
+ absent_fields = []
43
+ fields.each_pair do |key, opts|
44
+ if !form.has_key?(key) || form[key].nil?
45
+ if opts[:required]
46
+ absent_fields << key
47
+ end
48
+ next
49
+ end
50
+ if opts[:re]
51
+ if form[key] !~ opts[:re]
52
+ raise RegApi2::ContractError.new(
53
+ "Field #{key} mismatch regular expression: #{form[key]}"
54
+ )
55
+ end
56
+ end
57
+ end
58
+ unless absent_fields.empty?
59
+ raise RegApi2::ContractError.new(
60
+ "Required fields missed: #{absent_fields.join(', ')}"
61
+ )
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module RegApi2
3
+ # Contracts for API results.
4
+ # Take a look at {RegApi2::DEFAULT_RESULT_CONTRACT} for defaults.
5
+ module ResultContract
6
+ # Waits for answer field and returns it only.
7
+ class Default
8
+ attr_reader :opts
9
+
10
+ def initialize(opts = {})
11
+ @opts = opts
12
+ end
13
+
14
+ # Extracts answer field and returns it wrapped by {#handle_answer}.
15
+ def handle_result(result)
16
+ handle_answer(result['answer'])
17
+ end
18
+
19
+ # Return passed argument by default.
20
+ def handle_answer(answer)
21
+ answer
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'reg_api2/result_contract/default'
4
+
5
+ # Waits for single field in answer field and returns it only.
6
+ class RegApi2::ResultContract::SingleField < RegApi2::ResultContract::Default
7
+ def handle_answer answer
8
+ field = opts[:field]
9
+ unless answer[field]
10
+ raise RegApi2::ContractError.new(
11
+ "#{field} field should be found in API result."
12
+ )
13
+ end
14
+ answer[field]
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module RegApi2
3
+ # REG.API service category
4
+ module Service
5
+
6
+ include RegApi2::Builder
7
+
8
+ category :service
9
+
10
+ # @!method nop(opts = {})
11
+ # @param opts
12
+ # @option opts [Array] services
13
+ # Return list of specified services with its stats if specified.
14
+ # @return [Hash("services" => [])]
15
+ # @example List of services by specified identifiers
16
+ # RegApi2.service.nop(services: [
17
+ # { dname:"test.ru" },
18
+ # { dname: "test.su", servtype: "srv_hosting_ispmgr" },
19
+ # { service_id: 111111 },
20
+ # { service_id: "22bug22" },
21
+ # { surprise: "surprise.ru" }
22
+ # ])
23
+ define :nop
24
+
25
+ extend self
26
+ end
27
+ end