reg.api2 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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