hot_catch 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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +27 -0
- data/Rakefile +34 -0
- data/lib/generators/hot_catch/install_generator.rb +57 -0
- data/lib/generators/hot_catch/uninstall_generator.rb +28 -0
- data/lib/hot_catch.rb +3 -0
- data/lib/hot_catch/custom_log_subscribers.rb +67 -0
- data/lib/hot_catch/hot_catch_log.rb +60 -0
- data/lib/hot_catch/hot_catch_logger.rb +114 -0
- data/lib/hot_catch/version.rb +3 -0
- data/lib/tasks/hot_catch_tasks.rake +4 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ce4c89223725b3c189db6f67ccca14d5d3f2f92a
|
4
|
+
data.tar.gz: 6cb563238d8ee4e61b38900ada6e32909328f81a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a821681cac3f21fd3b43f1486a523e9adbe368af0b4b2abc6b0e7c0de6460d8ac269ad8cdc63e319fd9027d400102a0f644c82ae1ff2cae5e453a9b5aa51f62e
|
7
|
+
data.tar.gz: 2172336cfc52925ce9b3d1171d0d47024051350471d3fb5c4bd187df44226d205089177dd0e38eabefe012b89dee501addf7ba3b91409faedf2f20368c9873c5
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Davhot
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# HotCatch
|
2
|
+
Данный гем позволяет изменить логирование в базу данных.
|
3
|
+
В дальнейшем будет отправлять логи главному приложению.
|
4
|
+
|
5
|
+
## Использование
|
6
|
+
Логи можно посмотреть в консоли:
|
7
|
+
```ruby
|
8
|
+
> HotCatchLog.all
|
9
|
+
> HotCatchLog.first.log_data
|
10
|
+
```
|
11
|
+
В данной реализации логи разделены по статусам запросов (поле `status HotCatchLog`).
|
12
|
+
Кроме того одинаковые ошибки собираются в одну, при этом увеличивается счётчик в моделе (поле `count_log HotCatchLog`).
|
13
|
+
|
14
|
+
## Установка
|
15
|
+
1. Добавить гем в `Gemfile`
|
16
|
+
```ruby
|
17
|
+
gem 'hot_catch'
|
18
|
+
```
|
19
|
+
2. `$ bundle install`
|
20
|
+
3. Запустить генератор установки: `$ rails generate hot_catch:install`
|
21
|
+
Для удаления: `$ rails generate hot_catch:uninstall`
|
22
|
+
4. `$ rails db:migrate`
|
23
|
+
|
24
|
+
Это всё! Теперь все логи формируются в базе данных.
|
25
|
+
|
26
|
+
## License
|
27
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'HotCatch'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# rails generate testgem:install
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
module HotCatch
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
# Включение всех файлов из указанной папки относительно __FILE__ (то есть текущего файла)
|
8
|
+
source_root File.expand_path('../../../hot_catch', __FILE__)
|
9
|
+
# include Rails::Generators::Migration
|
10
|
+
|
11
|
+
def add_model_hot_catch
|
12
|
+
create_file "db/migrate/20170729154223_create_hot_catch_log.rb" do
|
13
|
+
<<RUBY
|
14
|
+
class CreateHotCatchLog < ActiveRecord::Migration[5.0]
|
15
|
+
def change
|
16
|
+
create_table :hot_catch_logs do |t|
|
17
|
+
t.string :log_data, null: false
|
18
|
+
t.integer :count_log, default: 1, null: false
|
19
|
+
t.string :status, null: false
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
end
|
27
|
+
|
28
|
+
create_file "app/models/application_record.rb" do
|
29
|
+
"class ApplicationRecord < ActiveRecord::Base
|
30
|
+
self.abstract_class = true
|
31
|
+
end"
|
32
|
+
end unless File.exist? "app/models/application_record.rb"
|
33
|
+
|
34
|
+
|
35
|
+
copy_file "hot_catch_log.rb", "app/models/hot_catch_log.rb"
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def add_rack_logger
|
40
|
+
copy_file "hot_catch_logger.rb", "config/hot_catch_logger.rb"
|
41
|
+
end
|
42
|
+
|
43
|
+
def change_files
|
44
|
+
insert_into_file "config/application.rb", "\nrequire_relative 'hot_catch_logger'\n", :before => "module ExampleApp"
|
45
|
+
|
46
|
+
application "config.middleware.insert_before Rails::Rack::Logger, Rails::Rack::HotCatchLogger\n
|
47
|
+
config.middleware.delete Rails::Rack::Logger"
|
48
|
+
|
49
|
+
insert_string = "\n require 'hot_catch/custom_log_subscribers.rb'
|
50
|
+
config.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new('hot_catch_buf_file'))\n"
|
51
|
+
|
52
|
+
insert_into_file "config/environments/development.rb", insert_string, :after => "Rails.application.configure do"
|
53
|
+
insert_into_file "config/environments/production.rb", insert_string, :after => "Rails.application.configure do"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# rails generate testgem:install
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
module HotCatch
|
6
|
+
class UninstallGenerator < Rails::Generators::Base
|
7
|
+
# include Rails::Generators::Migration
|
8
|
+
|
9
|
+
def remove_model_hot_catch
|
10
|
+
remove_file "db/migrate/20170729154223_create_hot_catch_log.rb"
|
11
|
+
remove_file "app/models/hot_catch_log.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove_rack_logger
|
15
|
+
remove_file "config/hot_catch_logger.rb"
|
16
|
+
end
|
17
|
+
|
18
|
+
def unchange_files
|
19
|
+
gsub_file "config/application.rb", /require_relative 'hot_catch_logger'/, ""
|
20
|
+
gsub_file "config/application.rb", /config.*Logger/, ""
|
21
|
+
|
22
|
+
gsub_file "config/environments/development.rb", /.*\s.*hot_catch_buf_file'\)\)/, ""
|
23
|
+
gsub_file "config/environments/production.rb", /.*\s.*hot_catch_buf_file'\)\)/, ""
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/hot_catch.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'active_record/log_subscriber'
|
2
|
+
|
3
|
+
class CustomActiveRecordLogSubscriber < ActiveRecord::LogSubscriber
|
4
|
+
# Убрана подсветка и не логируется таблица hot_catch_buf_file
|
5
|
+
def sql(event)
|
6
|
+
self.class.runtime += event.duration
|
7
|
+
return unless logger.debug?
|
8
|
+
|
9
|
+
payload = event.payload
|
10
|
+
|
11
|
+
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
12
|
+
|
13
|
+
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
14
|
+
sql = payload[:sql]
|
15
|
+
binds = nil
|
16
|
+
|
17
|
+
unless (payload[:binds] || []).empty?
|
18
|
+
casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
|
19
|
+
binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
|
20
|
+
render_bind(attr, value)
|
21
|
+
}.inspect
|
22
|
+
end
|
23
|
+
|
24
|
+
# name = colorize_payload_name(name, payload[:name])
|
25
|
+
# sql = color(sql, sql_color(sql), true)
|
26
|
+
|
27
|
+
debug " #{name} #{sql}#{binds}" unless payload[:sql].to_s =~ /hot_catch_logs|COMMIT|BEGIN/
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Отписка от уведомлений ActiveRecord::LogSubscriber
|
32
|
+
notifier = ActiveSupport::Notifications.notifier
|
33
|
+
subscribers = notifier.listeners_for("sql.active_record")
|
34
|
+
subscribers.each {|s| ActiveSupport::Notifications.unsubscribe s }
|
35
|
+
|
36
|
+
# Подписка на уведомления CustomActiveRecordLogSubscriber
|
37
|
+
CustomActiveRecordLogSubscriber.attach_to :active_record
|
38
|
+
# ====================================================================================
|
39
|
+
require 'action_controller/log_subscriber'
|
40
|
+
|
41
|
+
class CustomActionControllerLogSubscriber < ActionController::LogSubscriber
|
42
|
+
# Статус записывается в лог файл hot_catch_buf_file
|
43
|
+
def process_action(event)
|
44
|
+
payload = event.payload
|
45
|
+
additions = ActionController::Base.log_process_action(payload)
|
46
|
+
|
47
|
+
status = payload[:status]
|
48
|
+
if status.nil? && payload[:exception].present?
|
49
|
+
exception_class_name = payload[:exception].first
|
50
|
+
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
File.open('hot_catch_buf_file', 'a'){ |file| file.puts "!!!#{status}!!!" }
|
54
|
+
|
55
|
+
info do
|
56
|
+
|
57
|
+
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
58
|
+
message << " (#{additions.join(" | ")})" unless additions.blank?
|
59
|
+
message
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
subscribers = notifier.listeners_for("process_action.action_controller")
|
65
|
+
subscribers.each {|s| ActiveSupport::Notifications.unsubscribe s }
|
66
|
+
|
67
|
+
CustomActionControllerLogSubscriber.attach_to :action_controller
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class HotCatchLog < ApplicationRecord
|
2
|
+
|
3
|
+
validates :log_data, :status, presence: true
|
4
|
+
validates :count_log, presence: true, numericality: {only_integer: true, greater_than: 0}
|
5
|
+
|
6
|
+
STATUSES = ["STATUS", "SUCCESS", "REDIRECTION", "CLIENT_ERROR", "SERVER_ERROR", "WARNING"]
|
7
|
+
|
8
|
+
# Проверка лога на уникальность
|
9
|
+
def self.process_data_log(str)
|
10
|
+
str, status = get_status(str)
|
11
|
+
|
12
|
+
# Если статус не найден, то возможна ошибка в контроллере
|
13
|
+
status = STATUSES[3] if !status && str =~ /ActionController/
|
14
|
+
|
15
|
+
str2 = strip_str(str)
|
16
|
+
opt = true
|
17
|
+
|
18
|
+
HotCatchLog.where(status: status).each do |cur_log|
|
19
|
+
if strip_str(cur_log.log_data) == str2
|
20
|
+
cur_log.count_log += 1
|
21
|
+
cur_log.save
|
22
|
+
opt = false
|
23
|
+
return
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if opt && str.strip.present? && status.present?
|
28
|
+
HotCatchLog.create(log_data: format_log(str), status: status)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Метод для сравнения логов
|
36
|
+
def self.strip_str(str)
|
37
|
+
str = str.dup
|
38
|
+
str.gsub!(/\d+(\.\d+)?ms/, "") # За сколько выполнилось действие
|
39
|
+
str.gsub!(/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s\+\d{4}/, "") # дата
|
40
|
+
str.gsub!(/#<.*:0x[\d|\w]+>/, "") # Объект (например #<StaticPagesController:0x007f37791686d8>)
|
41
|
+
format_log(str)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Помимо сравнения логов, нужен для форматирования лога и записи в бд
|
45
|
+
def self.format_log(str)
|
46
|
+
str = str.split("\n").select{|x| x.present?}.join("\n") # убирает повторяющиеся переводы строки
|
47
|
+
str.strip! # лишние символы пропусков в начале и конце
|
48
|
+
str
|
49
|
+
end
|
50
|
+
|
51
|
+
# Возвращает статус и лог без статуса
|
52
|
+
def self.get_status(str)
|
53
|
+
status = str.match(/!!!(\d{3})!!!/)
|
54
|
+
status = status[1] if status
|
55
|
+
str = str.gsub(/!!!(\d{3})!!!\s/, "")
|
56
|
+
status = STATUSES[status[0].to_i - 1] if status.present?
|
57
|
+
[str, status]
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'active_support/core_ext/time/conversions'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
require 'active_support/log_subscriber'
|
4
|
+
require 'action_dispatch/http/request'
|
5
|
+
require 'rack/body_proxy'
|
6
|
+
# require 'rack/utils'
|
7
|
+
|
8
|
+
require_relative '../app/models/application_record.rb'
|
9
|
+
require_relative '../app/models/hot_catch_log.rb'
|
10
|
+
|
11
|
+
module Rails
|
12
|
+
module Rack
|
13
|
+
# Sets log tags, logs the request, calls the app, and flushes the logs.
|
14
|
+
#
|
15
|
+
# Log tags (+taggers+) can be an Array containing: methods that the +request+
|
16
|
+
# object responds to, objects that respond to +to_s+ or Proc objects that accept
|
17
|
+
# an instance of the +request+ object.
|
18
|
+
class HotCatchLogger < ActiveSupport::LogSubscriber
|
19
|
+
def initialize(app, taggers = nil)
|
20
|
+
@app = app
|
21
|
+
@taggers = taggers || []
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(env)
|
25
|
+
request = ActionDispatch::Request.new(env)
|
26
|
+
|
27
|
+
if logger.respond_to?(:tagged)
|
28
|
+
logger.tagged(compute_tags(request)) { call_app(request, env) }
|
29
|
+
else
|
30
|
+
call_app(request, env)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def call_app(request, env)
|
37
|
+
instrumenter = ActiveSupport::Notifications.instrumenter
|
38
|
+
instrumenter.start 'request.action_dispatch', request: request
|
39
|
+
|
40
|
+
# Флаг начала запроса
|
41
|
+
logger << "\nBEGIN\n"
|
42
|
+
|
43
|
+
logger.info { started_request_message(request) }
|
44
|
+
resp = @app.call(env)
|
45
|
+
resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
|
46
|
+
resp
|
47
|
+
rescue Exception
|
48
|
+
finish(request)
|
49
|
+
raise
|
50
|
+
ensure
|
51
|
+
ActiveSupport::LogSubscriber.flush_all!
|
52
|
+
end
|
53
|
+
|
54
|
+
# Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
|
55
|
+
def started_request_message(request)
|
56
|
+
'Started %s "%s" for %s at %s' % [
|
57
|
+
request.request_method,
|
58
|
+
request.filtered_path,
|
59
|
+
request.ip,
|
60
|
+
Time.now.to_default_s ]
|
61
|
+
end
|
62
|
+
|
63
|
+
def compute_tags(request)
|
64
|
+
@taggers.collect do |tag|
|
65
|
+
case tag
|
66
|
+
when Proc
|
67
|
+
tag.call(request)
|
68
|
+
when Symbol
|
69
|
+
request.send(tag)
|
70
|
+
else
|
71
|
+
tag
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def finish(request)
|
79
|
+
# Если быстро обновлять страницу, то конец лога не успевает записаться.
|
80
|
+
# Для этого записываем последний лог в новый файл и с новым запросом считываем
|
81
|
+
# его и записанный конец лога из изначального лог-файла.
|
82
|
+
|
83
|
+
# Считали данные из основного лог-файла
|
84
|
+
logs = ""
|
85
|
+
File.open('hot_catch_buf_file'){ |file| logs = file.read}
|
86
|
+
File.open('hot_catch_buf_file', 'w'){ |file| file.print "" }
|
87
|
+
logs = logs.split("BEGIN").delete_if{|x| !x.present? }
|
88
|
+
|
89
|
+
# Считали данные из дополнительного лог-файла и записали начало последнего лога
|
90
|
+
add_log = ""
|
91
|
+
File.open('hot_catch_buf_file2'){ |file| add_log = file.read} if File.exist?('hot_catch_buf_file2')
|
92
|
+
File.open('hot_catch_buf_file2', 'w'){ |file| file.print logs[-1] }
|
93
|
+
logs.delete_at(-1)
|
94
|
+
|
95
|
+
# Собираем начало лога и конец
|
96
|
+
add_log = add_log + (logs[0]).to_s
|
97
|
+
HotCatchLog.process_data_log(add_log) unless add_log.strip =~ /assets/
|
98
|
+
|
99
|
+
logs.delete_at(0)
|
100
|
+
# Обрабатываем остальные логи, если есть
|
101
|
+
logs.each {|log| HotCatchLog.process_data_log(log) unless log.strip =~ /assets/}
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
instrumenter = ActiveSupport::Notifications.instrumenter
|
106
|
+
instrumenter.finish 'request.action_dispatch', request: request
|
107
|
+
end
|
108
|
+
|
109
|
+
def logger
|
110
|
+
Rails.logger
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hot_catch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Davhot
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Description of Testgem.
|
14
|
+
email:
|
15
|
+
- david.home@mail.ru
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- MIT-LICENSE
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- lib/generators/hot_catch/install_generator.rb
|
24
|
+
- lib/generators/hot_catch/uninstall_generator.rb
|
25
|
+
- lib/hot_catch.rb
|
26
|
+
- lib/hot_catch/custom_log_subscribers.rb
|
27
|
+
- lib/hot_catch/hot_catch_log.rb
|
28
|
+
- lib/hot_catch/hot_catch_logger.rb
|
29
|
+
- lib/hot_catch/version.rb
|
30
|
+
- lib/tasks/hot_catch_tasks.rake
|
31
|
+
homepage: https://myhomepage.com
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.5.1
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Summary of Testgem.
|
55
|
+
test_files: []
|