tehportal 0.1.0

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