tehportal 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c5a8fa924393d1efda5585ea6666232b67310de
4
+ data.tar.gz: 5f6601b8bd473d1b691c56258bbe1a8814140a86
5
+ SHA512:
6
+ metadata.gz: 1392c6f43ed7369ccbc8b5ce71d26e34a0ade9363d5dd718df3a3d45e511bc19e0334aecc4bad58d81bcc8c907b1a2cda3d4828358b76150022cc6a11197849b
7
+ data.tar.gz: 2b70144a02c70d0a8fc3229a496751c94426fbcfc47f8115ace6182e7eab7c9e7184ce8fb4546311cff2e0da71680230e1ca85d7ef74febc18193f9c803ee2cf
data/.gitignore ADDED
@@ -0,0 +1,23 @@
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
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tehportal.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Tehportal
2
+
3
+ ## Описание
4
+ Данный gem это ядро для работы с remedy и возможно с другими системами.
5
+
6
+ ## Модели
7
+ Все сервиси группируем - инциденты, изменения, проблемы, задачи и тд. Для каждой группы делаем модель с базовым классом - **Tehportal::Model**.
8
+ В модели делаем описание всех сервисов в виде хэша:
9
+
10
+ @@services = {
11
+ show: 'ATC:HPD:AKEOS-Portal-Get-Incident-WorkInfo',
12
+ list: 'ATC:INC:AKEOS-List',
13
+ create: 'ATC:INC:AKEOS-Create',
14
+ update: 'ATC:INC:AKEOS-Modify'
15
+ }
16
+
17
+ Где ключ данного хэша будет соответствовать action-методу в соответствующем контроллере.
18
+ Также в базовом классе моделе реализован запрос к серверу(сейчас через method_missing, после рефакторинга с помощью class_eval).
19
+ Модели называем во множественном числе(например: Tehportal::Incidents), чтобы соответствовало названию контроллера.
20
+
21
+ **ВАЖНО:** Т.к. в remedy важен порядок передаваемых ему полей в моделе реализован механизм, который расставляем параметры в нжном порядке. Нужно просто передать параметры.
22
+
23
+ ### 1.Инциденты
24
+ Модель: **Tehportal::Incidents**
25
+ ### 2.Изменения
26
+ ### 3.Проблемы
27
+ Модель: **Tehportal::Problems**
28
+ ### 4.Релизы
29
+
30
+ ## Контроллеры/API
31
+ Все контроллеры для работы с remedy наследуются от **RemedyBaseController**. Перед запуском любого action-метода срабатывает хук который отпределяет
32
+ с какой моделью работать данному контроллеру.
33
+
34
+ Для каждого сервиса реализуется action-метод, в который в качестве параметра передается название операции и другие параметры.
35
+
36
+ Базовый url: _/tp/remedy_
37
+
38
+ Параметры который пойдут на вход Remedy передаются в массиве data (пример: data[ID]=23&data[Description]=New_Message)
39
+ Для action может быть несколько операций, они передаются параметром operation(пример: operation=INCGetList-InWork)
40
+
41
+ ### 1.Инциденты
42
+ #### Список инцидентов
43
+
44
+ *URL:* GET /incidents/
45
+
46
+ |Операция|Описание|Параметры|
47
+ |--------|--------|---------|
48
+ | _INCGetList-InWork_ | Назначенные на меня со статусом "открытые" |startRecord - offset для списка<br /> maxLimit - limit для списка<br />Login - логин пользователя|
49
+ | _INCGetList-SLABreached-My_ | Назначенные на меня со статусом "нарушенные" |startRecord - offset для списка<br /> maxLimit - limit для списка<br />Login - логин пользователя|
50
+ | _INCGetList-Pending-My_ | Назначенные на меня со статусом "в ожидании" |startRecord - offset для списка<br /> maxLimit - limit для списка<br />Login - логин пользователя|
51
+ | _INC_Created_By_Me_InWork_ | Созданные мной |startRecord - offset для списка<br /> maxLimit - limit для списка<br />Login - логин пользователя|
52
+ | _INCGetList-SLABreached-OnMyGroup_ | Назначенные на группу со статусом "просроченные"|startRecord - offset для списка<br /> maxLimit - limit для списка<br />Group - Название группы|
53
+ | _INCGetList-InWork-OnMyGroup_ | Назначенные на группу со статусом "открытые"|startRecord - offset для списка<br /> maxLimit - limit для списка<br />Group - Название группы|
54
+ | _NCGetList-Pending-OnMyGroup_ | Назначенные на группу со статусом "в ожидании" |startRecord - offset для списка<br /> maxLimit - limit для списка<br />Group - Название группы|
55
+
56
+ ##### *URL:* GET /incidents/:id - Просмотр инцидента
57
+ ##### *URL:* GET /incidents/:id/associations - Просмотр инцидента: свзяи
58
+ ##### *URL:* GET /incidents/:id/tasks - Просмотр инцидента: задачи
59
+ ##### *URL:* GET /incidents/:id/times - Просмотр инцидента: затраченное время
60
+
61
+ ### 2.Задачи
62
+ #### Список задач
63
+
64
+ *URL:* GET /tasks/
65
+
66
+ |Операция|Описание|Параметры|
67
+ |--------|--------|---------|
68
+ | _TSK_My_InWork_ | Назначенные на меня со статусом "открытые" |startRecord - offset для списка<br /> maxLimit - limit для списка<br />Login - логин пользователя|
69
+ | _TSK_Created_By_Me_InWork_ | Созданные мной |startRecord - offset для списка<br /> maxLimit - limit для списка<br />Login - логин пользователя|
70
+ | _TSK_OnMyGroup_InWork_ | Назначенные на группу со статусом "открытые"|startRecord - offset для списка<br /> maxLimit - limit для списка<br />Group - Название группы|
71
+
72
+ ##### *URL:* GET /incidents/:id - Просмотр задачи
73
+ ##### *URL:* GET /tasks/:id/comments - Просмотр задачи: комментарии
74
+ ##### *URL:* GET /tasks/:id/times - Просмотр задачи: затраченное время
75
+
76
+
77
+ ### 3.Изменения
78
+ ### 4.Проблемы
79
+ Контроллер: **ProblemsController**
80
+ Методы:
81
+
82
+ * list
83
+
84
+ команда:
85
+ `GET /tp/remedy/problems?operation=[операция]`
86
+
87
+ операции:
88
+
89
+ - InWorkMy
90
+ - InWorkGroup
91
+ - InWorkAllGroups
92
+
93
+ * show
94
+ команда:
95
+ `GET /tp/remedy/problems/[id]`
96
+
97
+ * create
98
+ команда:
99
+ `POST /tp/remedy/problems`
100
+
101
+ ### 5.Релизы
102
+
103
+ # Установка
104
+
105
+ 1. Добавить следующую строчку в Gemfile:
106
+
107
+ `gem 'tehportal', git: 'git@gitlab.at-consulting:dfp/tehportal'`
108
+
109
+ 1. В приложении необходимо создать инициализатор gem'а:
110
+
111
+ `Tehportal.configure do |config|
112
+ config.url = 'http://example.com'
113
+ config.server = 'example-server'
114
+ config.endpoint = 'http://example.com/services/ARService'
115
+ end`
116
+
117
+ 1. Роутинг:
118
+
119
+ `mount Tehportal::Engine => '/'`
120
+
121
+ 1. Авторизация: перед выполением каких либо действий нужно настроить пользователя от чьего имени будут выполняться запросы, если используется devise, то код будет следующего вида:
122
+
123
+ `Tehportal.setup_client(current_user.login, current_user.decrypt_password) if current_user`
124
+
125
+
126
+ # Использование
127
+
128
+ TODO: Write usage instructions here
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,59 @@
1
+ #++
2
+ # Базовый контроллер для всех remedy-запросов.
3
+ #
4
+ # Базовый action-методы описываются тут, если в конкретном подклассе необходимо,
5
+ # то всегда базовый метод можно переопределить.
6
+ #--
7
+ class RemedyController < Tehportal.config.parent_controller.constantize
8
+ # before_filter :specify_params, only: %i(index search)
9
+ # respond_with_params only: %i(index search)
10
+
11
+ def send_request
12
+ model = "Tehportal::#{params[:model].capitalize}".constantize
13
+ render_remedy_json model.send(params[:method], params[:data])
14
+ end
15
+
16
+ # ВСЕВОЗМОЖНЫЕ СПИСКИ
17
+ def index
18
+ result = model.list(params[:operation], list_params)
19
+ result['getListValues'] = Array.wrap(result['getListValues'])
20
+
21
+ render_remedy_json result
22
+ end
23
+
24
+ # СВЯЗИ
25
+ def associations
26
+ render_remedy_json model.associations('GetList_Association', association_params)
27
+ end
28
+
29
+ private
30
+
31
+ # Получение класса модели, через которую будут делаться запросы
32
+ def model
33
+ @model ||= "Tehportal::#{params[:controller].split('/').last.capitalize}".constantize
34
+ end
35
+
36
+ def model_class(name)
37
+ "Tehportal::#{name.capitalize}".constantize
38
+ end
39
+
40
+ # Запрос на получение связей, имя ID объекта у всех разный
41
+ def association_params
42
+ data = {}
43
+ model_name = params[:controller].split('/').last.downcase.to_sym
44
+ params_map = {incidents: 'IncidentNumber', problems: 'ProblemInvestigationID', changes: 'Infrastructure_Change_ID'}
45
+
46
+ data[params_map[model_name]] = params[:id]
47
+ data
48
+ end
49
+
50
+ private
51
+
52
+ def render_remedy_json(data)
53
+ prepared = data.except('@xmlns:ns0', '@xmlns:xsd', '@xmlns:xsi')
54
+ render json: (prepared.blank? && data.length != 3) ? nil : prepared
55
+ end
56
+
57
+ end
58
+
59
+
data/config/config.yml ADDED
File without changes
@@ -0,0 +1,2 @@
1
+ ru:
2
+ errors:
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ Tehportal::Engine.routes.draw do
2
+ # Модули для работы с Remedy
3
+ namespace :remedy do
4
+ scope :v1 do
5
+ post ':model/:method', action: 'send_request'
6
+ end
7
+ end
8
+ end
data/lib/nori_patch.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'nori/parser/nokogiri'
2
+
3
+ # В текущей реализации парсер пропускает пробелы между
4
+ # представленными в виде xml character entities non-ASCII словами,
5
+ # считая их мусором:
6
+ # <ns0:ASSIGNEDGROUP>&#x414;&#x435;&#x436;&#x443;&#x440;&#x43D;&#x430;&#x44F; &#x441;&#x43C;&#x435;&#x43D;&#x430; &#x41B;&#x41F;1.5</ns0:ASSIGNEDGROUP>
7
+ # Ожидается: "ns0:ASSIGNEDGROUP"=>"Дежурная смена ЛП1.5"
8
+ # Имеется в nori-2.4.0: "ns0:ASSIGNEDGROUP"=>"ДежурнаясменаЛП1.5"
9
+ class Nori::Parser::Nokogiri::Document
10
+
11
+ def characters(string)
12
+ last = stack.last
13
+ if last and last.children.last.is_a?(String) or string.strip.size > 0
14
+ last.add_node(string)
15
+ end
16
+ end
17
+ alias cdata_block characters
18
+
19
+ def end_element(name)
20
+ if stack.size > 1
21
+ last = stack.pop
22
+ maybe_string = last.children.last
23
+ if maybe_string.is_a?(String) and maybe_string.strip.empty?
24
+ last.children.pop
25
+ end
26
+ stack.last.add_node last
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,62 @@
1
+ module Tehportal
2
+ module Attachment
3
+ module_function
4
+
5
+ def file_list(path)
6
+ file_list = []
7
+ if path.present? and File.directory?(path)
8
+ file_list = Dir(path).content.select {|f| File.file?(f)}.map {|f| File.basename(f)}
9
+ end
10
+ file_list
11
+ end
12
+
13
+ def path_for(request_params, user)
14
+ if request_params[:commentable]
15
+ subject, id = request_params[:commentable]/'/'
16
+ else
17
+ subject = request_params[:controller]
18
+ if request_params[:action] == 'create'
19
+ id = 'NEW'
20
+ else
21
+ id = request_params[:id]
22
+ end
23
+ end
24
+ if subject
25
+ "#{Rails.root}/tmp/#{subject}/#{id}/#{user.login}"
26
+ end
27
+ end
28
+
29
+ def file_list_for(request_params, user)
30
+ path = path_for(request_params, user)
31
+ Attachment.file_list path
32
+ end
33
+
34
+ def collect_files_at(path)
35
+ files = {dir: path}
36
+
37
+ if File.directory?(path)
38
+ file_list = Attachment.file_list(path)
39
+ file_list[0..2].each_with_index do |file_name, index|
40
+ i = index+1
41
+ if content = read("#{path}/#{file_name}")
42
+ files.merge! :"name_#{i}" => file_name,
43
+ :"data_#{i}" => Base64.encode64(content),
44
+ :"size_#{i}" => content.size
45
+ end
46
+ end
47
+ end
48
+
49
+ files
50
+ end
51
+
52
+ def collect_files_for(request_params, user)
53
+ collect_files_at path_for(request_params, user)
54
+ end
55
+
56
+ def clear(params, user)
57
+ path_tmp = path_for(params, user)
58
+ FileUtils.rm_rf(path_tmp) if File.directory?(path_tmp)
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,5 @@
1
+ module Tehportal
2
+ class Configuration
3
+ attr_accessor :url, :endpoint, :server, :parent_controller
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ require 'rails/engine'
2
+
3
+ module Tehportal
4
+ class Engine < Rails::Engine
5
+ engine_name 'tp'
6
+
7
+ config.autoload_paths += %W(#{config.root}/models/**)
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ require 'tehportal/operation'
2
+
3
+ module Tehportal
4
+ class Model
5
+ extend ActiveModel::Naming
6
+ include Operation
7
+
8
+ @operations = {}
9
+
10
+ class << self
11
+ attr_accessor :operations
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # Орерации(действия)
2
+ module Operation
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ # Метод описания операции
9
+ # @ name : Название операции
10
+ # @ url : url = название_сервиса + / + название_операции
11
+ # @ options : Параметры для операции
12
+ # - defaults : Значения по умолчанию для Body
13
+ def operation(name, url, options = {})
14
+ options[:defaults] ||= {}
15
+ service, operation = url.split('/')
16
+ model_name = self.name.split('::').last.downcase
17
+
18
+ Tehportal::Model.operations[model_name] ||= {}
19
+ Tehportal::Model.operations[model_name][name] = {service: service, operation: operation}
20
+
21
+ # Добавляем синглтон метод к классу модели
22
+ define_singleton_method(name) do |*args|
23
+ client = args[0].delete(:client) || Tehportal.client
24
+ message = options[:defaults].merge(args[0])
25
+
26
+ client.request(service, operation, message)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,111 @@
1
+ module Tehportal
2
+ class RemedyClient
3
+ delegate :config, to: :Tehportal
4
+
5
+ def initialize(login = nil, password = nil)
6
+ @login, @password = login, password
7
+ @definitions = Rails.cache.read('tp.remedy.definitions') || {}
8
+ end
9
+
10
+ # Выполнение запроса к сервису
11
+ #
12
+ # @ service : Веб-сервис
13
+ # @ method : операция веб-сервиса
14
+ # @ body : хеш данных для запроса к веб-сервису
15
+ # e.g.: {"Status" => "Pending"}
16
+ def request(service, method, body = {})
17
+ message = {}
18
+ operation_map = operation_map(service, method)
19
+
20
+ operation_map[:input].each do |key|
21
+ message[key] = body[key] if body.has_key?(key) && body[key].to_s.downcase != 'null'
22
+ end unless operation_map.nil?
23
+
24
+ client = Savon.client(endpoint: "#{config.endpoint}?server=#{config.server}&webService=#{service}",
25
+ namespace: service,
26
+ soap_header: {'AuthenticationInfo' => {userName: @login, password: @password}},
27
+ ssl_verify_mode: :none,
28
+ raise_errors: false,
29
+ convert_response_tags_to: nil,
30
+ open_timeout: 300,
31
+ read_timeout: 300,
32
+ log: true,
33
+ logger: Rails.logger,
34
+ log_level: :debug,
35
+ filters: [:password])
36
+
37
+ response = client.call(method, message: message, soap_action: "urn:#{service}/#{method}", advanced_typecasting: false)
38
+
39
+ hash = response.to_hash
40
+ hash.has_key?('Fault') ? {errorMessage: hash['Fault']['faultstring']} : hash["#{method.gsub(/-/, '_')}Response"]
41
+ end
42
+
43
+ def wsdl_url(service)
44
+ [config.url, config.server, service].join('/')
45
+ end
46
+
47
+ # Получить Nori-made хеш на основе xml-документа <wsdl:definitions>
48
+ # e.g.: http://188.254.6.21/arsys/WSDL/public/skuf-app/ATC:INC:AKEOS-List
49
+ #
50
+ # @ service : имя сервиса
51
+ # e.g.: ATC:INC:AKEOS-List
52
+ def definitions(service)
53
+ unless @definitions[service]
54
+ @definitions[service] ||= begin
55
+ client = Savon.client({wsdl: wsdl_url(service),
56
+ ssl_verify_mode: :none,
57
+ raise_errors: false,
58
+ convert_request_keys_to: :none,
59
+ convert_response_tags_to: :none})
60
+ client.operations
61
+ xml = client.instance_variable_get(:@wsdl).xml
62
+ Nori.new(strip_namespaces: true, delete_namespace_attributes: true).parse(xml)['definitions']
63
+ end
64
+
65
+ Rails.cache.write('tp.remedy.definitions', @definitions, expires_in: 1.day)
66
+ end
67
+
68
+ @definitions[service]
69
+ end
70
+
71
+ # Определение доступных операций и их характеристик
72
+ # в соответствии с wsdl:definition по указанному сервису
73
+ #
74
+ # @ service : путь к значению в конфиге
75
+ # e.g.: ATC:INC:AKEOS-List
76
+ def operations_map(service)
77
+ defs = definitions(service)
78
+ schema = defs['types']['schema']
79
+ messages = Array.wrap(defs['message'])
80
+ elements = Array.wrap(schema['element'])
81
+ types = Array.wrap(schema['complexType'])
82
+ operations = Array.wrap(defs['portType']['operation'])
83
+
84
+ res = operations.map do |op|
85
+ op_name = op['@name']
86
+
87
+ message_name = op['output']['@message'].sub(/^[^:]+:/, '')
88
+ message = messages.find {|m| m['@name'] == message_name}
89
+ element_name = message['part']['@element'].sub(/^[^:]+:/, '')
90
+ # undasherize для удобства обхода (совместимо с текущим кодом)
91
+ element_name.tr! '-', '_'
92
+
93
+ type_name = elements.find {|e| e['@name'] == op_name}['@type'].sub(/^[^:]+:/, '')
94
+ type_input = types.find {|t| t['@name'] == type_name}['sequence']
95
+ if type_input
96
+ fields = Array.wrap(type_input['element'])
97
+ field_names = fields.map {|f| f['@name']}
98
+ end
99
+
100
+ [op_name, {output: element_name, input: Array.wrap(field_names)}]
101
+ end
102
+
103
+ Hash[res]
104
+ end
105
+
106
+ def operation_map(service, operation)
107
+ all_operations = operations_map(service)
108
+ all_operations[operation]
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ module Tehportal
2
+ VERSION = '0.1.0'
3
+ end
data/lib/tehportal.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'rails/all'
2
+ require 'savon'
3
+
4
+ require 'nori_patch'
5
+ require 'tehportal/engine'
6
+
7
+ # --
8
+ # Обертка для запросов к бекенду Техпортала (Ремеди)
9
+ # ++
10
+ module Tehportal
11
+ extend ActiveSupport::Autoload
12
+
13
+ autoload :Attachment
14
+ autoload :Configuration
15
+ autoload :Model
16
+ autoload :RemedyClient
17
+
18
+ class << self
19
+ attr_writer :config
20
+ attr_reader :client
21
+
22
+ delegate :request, to: :client
23
+
24
+ def configure
25
+ yield(config)
26
+ end
27
+
28
+ def config
29
+ @config ||= Configuration.new
30
+ end
31
+
32
+ def setup_client(login, password)
33
+ @client = RemedyClient.new(login, password)
34
+ end
35
+ end
36
+ end
37
+
38
+ require 'tehportal/version'
39
+
data/tehportal.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tehportal/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'tehportal'
8
+ spec.version = Tehportal::VERSION
9
+ spec.authors = ['AT Consulting']
10
+ spec.email = ['info@at-consulting.ru']
11
+ spec.summary = %q{Прослойка для работы с Remedy.}
12
+ spec.description = %q{Прослойка для работы с Remedy.}
13
+ spec.homepage = 'https://at-consulting.ru'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'savon', '~> 2.0'
21
+ spec.add_development_dependency 'bundler', '~> 1.6'
22
+ spec.add_development_dependency 'rake'
23
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tehportal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - AT Consulting
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: savon
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: "Прослойка для работы с Remedy."
56
+ email:
57
+ - info@at-consulting.ru
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - README.md
65
+ - Rakefile
66
+ - app/controllers/remedy_controller.rb
67
+ - config/config.yml
68
+ - config/locales/ru.yml
69
+ - config/routes.rb
70
+ - lib/nori_patch.rb
71
+ - lib/tehportal.rb
72
+ - lib/tehportal/attachment.rb
73
+ - lib/tehportal/configuration.rb
74
+ - lib/tehportal/engine.rb
75
+ - lib/tehportal/model.rb
76
+ - lib/tehportal/operation.rb
77
+ - lib/tehportal/remedy_client.rb
78
+ - lib/tehportal/version.rb
79
+ - tehportal.gemspec
80
+ homepage: https://at-consulting.ru
81
+ licenses: []
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.2.2
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: "Прослойка для работы с Remedy."
103
+ test_files: []