jetmeter 0.5.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: 552f4e1368015da59b856cf64cb93ad98b571ffd
4
+ data.tar.gz: f1ed9aa5f81412a7581337c010a0d5ddddb69d0c
5
+ SHA512:
6
+ metadata.gz: 144711bf099df07a7dc65e781bacd1436344428f5aa51f293fda266d8735f360174cc61f5048f63cf95f407fa6ace14f40cb6edc056ffe7c9ef6b6f0efbbbc31
7
+ data.tar.gz: '02394680fcb5c414938af6bab035f028c924fd6c9d39a7af0897ec3e8d55974e28183bf2479f541ae741a2ac769ce42a98f08ae37c314963393ae3ef522d487b'
data/bin/jetmeter ADDED
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env ruby
2
+ require 'jetmeter'
3
+
4
+ config_path = File.expand_path(ARGV[0])
5
+ Jetmeter::CLI.new(config_path).run
@@ -0,0 +1,117 @@
1
+ module Jetmeter
2
+ class CLI
3
+ CREDENTIAL_PATH = File.expand_path('~/.jetmeter/token').freeze
4
+ CACHE_PATH = File.expand_path('~/.jetmeter/cache').freeze
5
+
6
+ def initialize(config_path)
7
+ @config = eval(File.read(config_path))
8
+ prepare_cache
9
+ authenticate_user
10
+ end
11
+
12
+ def run
13
+ puts "Receiving issue events..."
14
+ repository_issue_events = Jetmeter::Collection.new(
15
+ Jetmeter::RepositoryIssueEventsLoader.new(@config).load,
16
+ Jetmeter::IssueEventAdapter
17
+ )
18
+
19
+ puts "Receiving issues..."
20
+ repository_issues = Jetmeter::Collection.new(
21
+ Jetmeter::RepositoryIssuesLoader.new(@config).load,
22
+ Jetmeter::IssueAdapter
23
+ )
24
+
25
+ accums = [
26
+ Jetmeter::OpenAccumulator.new,
27
+ Jetmeter::LabelAccumulator.new,
28
+ Jetmeter::LabelAccumulator.new(additive: false),
29
+ Jetmeter::CloseAccumulator.new,
30
+ Jetmeter::CloseAccumulator.new(additive: false),
31
+ Jetmeter::MergeAccumulator.new
32
+ ]
33
+ filters = [
34
+ Jetmeter::DateFilter.new,
35
+ Jetmeter::OpenFilter.new
36
+ ]
37
+
38
+ reducer = Jetmeter::FlowReducer.new(
39
+ [repository_issues, repository_issue_events],
40
+ @config
41
+ )
42
+
43
+ puts "Analyzing received data..."
44
+ reducer.reduce(accums, filters)
45
+
46
+ puts "Initializing CSV formatter..."
47
+ formatter = Jetmeter::CsvFormatter.new(@config, reducer)
48
+
49
+ puts "Saving CSV file..."
50
+ File.open(@config.output_path, 'wb') do |file|
51
+ formatter.save(file)
52
+ puts "Created CSV: #{@config.output_path}"
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def authenticate_user
59
+ if access_token_stored? && access_token_readable?
60
+ @config.github_credentials = { access_token: File.read(CREDENTIAL_PATH) }
61
+ else
62
+ login, password = ask_credentials
63
+ @config.github_credentials = { login: login, password: password }
64
+
65
+ authorization = create_authorization
66
+ save_access_token(authorization.token)
67
+ end
68
+ end
69
+
70
+ def prepare_cache
71
+ FileUtils.mkdir_p CACHE_PATH
72
+ @config.cache_path = CACHE_PATH
73
+ end
74
+
75
+ def ask_credentials
76
+ puts "Your github login:"
77
+ login = STDIN.gets.chomp
78
+
79
+ puts "Your github password:"
80
+ password = STDIN.noecho(&:gets).chomp
81
+
82
+ [login, password]
83
+ end
84
+
85
+ def ask_two_factor
86
+ puts 'Enter 2-factor authentication token:'
87
+ STDIN.gets.chomp
88
+ end
89
+
90
+ def create_authorization
91
+ auth_note = "jetmeter for #{ENV['USER']}@#{ENV['HOSTNAME']}"
92
+ @config.client.create_authorization(
93
+ scopes: [:repo],
94
+ note: auth_note
95
+ )
96
+ rescue Octokit::OneTimePasswordRequired
97
+ @config.client.create_authorization(
98
+ scopes: [:repo],
99
+ note: auth_note,
100
+ headers: { 'X-GitHub-OTP' => ask_two_factor }
101
+ )
102
+ end
103
+
104
+ def save_access_token(token)
105
+ File.write(CREDENTIAL_PATH, token)
106
+ @config.github_credentials = { access_token: token }
107
+ end
108
+
109
+ def access_token_stored?
110
+ File.exist?(CREDENTIAL_PATH)
111
+ end
112
+
113
+ def access_token_readable?
114
+ File.readable?(CREDENTIAL_PATH)
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,25 @@
1
+ module Jetmeter
2
+ class CloseAccumulator
3
+ CLOSED_EVENT = 'closed'.freeze
4
+
5
+ attr_reader :additive
6
+
7
+ def initialize(additive: true)
8
+ @additive = additive
9
+ end
10
+
11
+ def valid?(event, flow)
12
+ event.issue_event? &&
13
+ event.event == CLOSED_EVENT &&
14
+ closing_transition?(flow)
15
+ end
16
+
17
+ private
18
+
19
+ def closing_transition?(flow)
20
+ flow.transitions(additive).any? do |from, to|
21
+ from.nil? && to.include?(:closed)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module Jetmeter
2
+ class Collection < SimpleDelegator
3
+ def initialize(inner_collection, adapter_class)
4
+ super(inner_collection.map { |el| adapter_class.new(el) })
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ module Jetmeter
2
+ class Config
3
+ class ClientMiddleware
4
+ def self.build(cache_path)
5
+ raise ArgumentError unless cache_path
6
+
7
+ store = Jetmeter::Config::FileCacheStore.new(cache_path)
8
+
9
+ Faraday::RackBuilder.new do |builder|
10
+ builder.use Faraday::HttpCache, store: store, serializer: Marshal, shared_cache: false
11
+ builder.use Octokit::Response::RaiseError
12
+ builder.adapter Faraday.default_adapter
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ module Jetmeter
2
+ class Config
3
+ class FileCacheStore
4
+ def initialize(root_path)
5
+ raise ArgumentError unless File.exist?(root_path)
6
+ raise ArgumentError unless File.directory?(root_path)
7
+ raise ArgumentError unless File.writable?(root_path)
8
+
9
+ @root_path = root_path
10
+ end
11
+
12
+ def read(key)
13
+ if readable?(key)
14
+ File.open(path(key)) { |f| Marshal.load(f) }
15
+ end
16
+ end
17
+
18
+ def write(key, value)
19
+ File.open(path(key), 'wb') { |f| Marshal.dump(value, f) }
20
+ end
21
+
22
+ def delete(key)
23
+ if File.exist?(path(key))
24
+ File.delete(path(key))
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def path(key)
31
+ File.join(@root_path, Digest::MD5.hexdigest(key))
32
+ end
33
+
34
+ def readable?(key)
35
+ File.exist?(path(key)) && File.readable?(path(key))
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ module Jetmeter
2
+ class Config
3
+ class Flow
4
+ attr_reader :additions
5
+ attr_reader :substractions
6
+ attr_accessor :filters
7
+ attr_writer :accumulative
8
+
9
+ def initialize
10
+ @additions = Hash.new { |hash, key| hash[key] = [] }
11
+ @substractions = Hash.new { |hash, key| hash[key] = [] }
12
+ @filters = {}
13
+ @accumulative = true
14
+ end
15
+
16
+ def register_addition(hash)
17
+ hash.each_pair do |key, value|
18
+ @additions[key] << value
19
+ end
20
+ end
21
+
22
+ def register_substraction(hash)
23
+ hash.each_pair do |key, value|
24
+ @substractions[key] << value
25
+ end
26
+ end
27
+
28
+ def transitions(additive)
29
+ additive ? additions : substractions
30
+ end
31
+
32
+ def accumulative?
33
+ !!@accumulative
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module Jetmeter
2
+ class Config
3
+ attr_accessor :repository_name
4
+ attr_accessor :output_path
5
+ attr_reader :flows
6
+ attr_writer :cache_path
7
+
8
+ def initialize(api: Octokit::Client, middleware: Jetmeter::Config::ClientMiddleware)
9
+ raise ArgumentError unless block_given?
10
+
11
+ @api = api
12
+ @middleware = middleware
13
+ @flows = {}
14
+ @cache_path = nil
15
+
16
+ yield self
17
+ end
18
+
19
+ def github_credentials=(credentials)
20
+ @github_credentials = credentials
21
+ @_client = nil
22
+ end
23
+
24
+ def client
25
+ @_client ||= begin
26
+ client = @api.new(@github_credentials)
27
+ client.auto_paginate = true
28
+ client.middleware = @middleware.build(@cache_path) if @cache_path
29
+ client
30
+ end
31
+ end
32
+
33
+ def register_flow(flow_name, &block)
34
+ @flows[flow_name] = Jetmeter::Config::Flow.new.tap(&block)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ module Jetmeter
2
+ class CsvFormatter
3
+ def initialize(config, reducer)
4
+ @config = config
5
+ @reducer = reducer
6
+ @commulative = Hash.new { |hash, flow| hash[flow] = Set.new }
7
+ @dates = @reducer.flows.values.map(&:keys).flatten
8
+ end
9
+
10
+ def save(io)
11
+ csv = CSV.new(io)
12
+ render_header(csv)
13
+ render_rows(csv)
14
+ end
15
+
16
+ private
17
+
18
+ def render_header(csv)
19
+ csv << ['Date'] + @config.flows.keys
20
+ end
21
+
22
+ def render_rows(csv)
23
+ return if @dates.empty?
24
+
25
+ (@dates.min..@dates.max).each do |date|
26
+ accumulative_flow_names = []
27
+
28
+ @config.flows.each_pair do |flow_name, flow_config|
29
+ issues = Set.new(@reducer.flows[flow_name][date])
30
+
31
+ @commulative[flow_name] |= issues
32
+
33
+ accumulative_flow_names.each do |accumulative_flow_name|
34
+ @commulative[accumulative_flow_name] |= issues
35
+ end
36
+
37
+ if flow_config.accumulative?
38
+ accumulative_flow_names.push(flow_name)
39
+ end
40
+ end
41
+
42
+ csv << [date.iso8601] + @commulative.values.map(&:size)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ module Jetmeter
2
+ class DateFilter
3
+ def apply?(resource, flow)
4
+ flow.filters.key?(:start_at) && resource.created_at < flow.filters[:start_at]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ module Jetmeter
2
+ class FlowReducer
3
+ attr_reader :flows
4
+
5
+ def initialize(resource_collections, config)
6
+ @resource_collections = resource_collections
7
+ @config_flows = config.flows
8
+ @flows = Hash.new do |hash, flow_name|
9
+ hash[flow_name] = Hash.new { |flow, date| flow[date] = [] }
10
+ end
11
+ end
12
+
13
+ def reduce(accumulators, filters)
14
+ @resource_collections.each do |resource_collection|
15
+ resource_collection.each do |resource|
16
+ reduce_resource(resource, accumulators, filters)
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def reduce_resource(resource, accumulators, filters)
24
+ @config_flows.each_pair do |flow_name, flow_config|
25
+ next if filters.any? { |filter| filter.apply?(resource, flow_config) }
26
+
27
+ accumulators.each do |accumulator|
28
+ if accumulator.valid?(resource, flow_config)
29
+ apply_accumulator(resource, accumulator, flow_name)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def apply_accumulator(resource, accumulator, flow_name)
36
+ if accumulator.additive
37
+ @flows[flow_name][resource.flow_date].push(resource.issue_number)
38
+ else
39
+ @flows[flow_name][resource.flow_date].delete(resource.issue_number)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ require 'jetmeter/resource_adapter'
2
+
3
+ module Jetmeter
4
+ class IssueAdapter < ResourceAdapter
5
+ def issue?
6
+ true
7
+ end
8
+
9
+ def flow_date
10
+ created_at.to_date
11
+ end
12
+
13
+ def issue_number
14
+ number
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'jetmeter/resource_adapter'
2
+
3
+ module Jetmeter
4
+ class IssueEventAdapter < ResourceAdapter
5
+ def issue_event?
6
+ true
7
+ end
8
+
9
+ def flow_date
10
+ created_at.to_date
11
+ end
12
+
13
+ def issue_number
14
+ issue[:number]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,64 @@
1
+ module Jetmeter
2
+ class LabelAccumulator
3
+ LABELED_EVENT = 'labeled'.freeze
4
+ UNLABELED_EVENT = 'unlabeled'.freeze
5
+ CORRESPONDING_EVENTS_LIMIT = 15
6
+ MAX_LABEL_CHANGE_TIME = 60
7
+
8
+ attr_reader :additive
9
+
10
+ def initialize(additive: true)
11
+ @corresponding_events = { LABELED_EVENT => [], UNLABELED_EVENT => [] }
12
+ @additive = additive
13
+ end
14
+
15
+ def valid?(event, flow)
16
+ return false unless event.issue_event?
17
+
18
+ store_corresponding_event(event)
19
+ labeling_transition?(flow, event) || unlabeling_transition?(flow, event)
20
+ end
21
+
22
+ private
23
+
24
+ def labeling_transition?(flow, event)
25
+ if event.event == LABELED_EVENT
26
+ flow.transitions(additive).any? do |from, to|
27
+ to.include?(event.label[:name]) && from.nil? || corresponding_event?(event, from)
28
+ end
29
+ end
30
+ end
31
+
32
+ def unlabeling_transition?(flow, event)
33
+ if event.event == UNLABELED_EVENT
34
+ flow.transitions(additive).any? do |from, to|
35
+ from == event.label[:name] && to.any? { |label| label.nil? || corresponding_event?(event, label) }
36
+ end
37
+ end
38
+ end
39
+
40
+ def store_corresponding_event(event)
41
+ return unless [LABELED_EVENT, UNLABELED_EVENT].include?(event.event)
42
+
43
+ @corresponding_events[event.event].push(event)
44
+ if @corresponding_events[event.event].length > CORRESPONDING_EVENTS_LIMIT
45
+ @corresponding_events[event.event].shift
46
+ end
47
+ end
48
+
49
+ def corresponding_event?(event, label)
50
+ corresponding_type = event.event == LABELED_EVENT ? UNLABELED_EVENT : LABELED_EVENT
51
+
52
+ @corresponding_events[corresponding_type].any? do |corresponding|
53
+ corresponding.label[:name] == label &&
54
+ corresponding.issue[:number] == event.issue[:number] &&
55
+ near?(corresponding, event)
56
+ end
57
+ end
58
+
59
+ def near?(first, second)
60
+ diff = (first.created_at.to_time - second.created_at.to_time).abs
61
+ diff < MAX_LABEL_CHANGE_TIME
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,23 @@
1
+ module Jetmeter
2
+ class MergeAccumulator
3
+ MERGED_EVENT = 'merged'.freeze
4
+
5
+ def valid?(event, flow)
6
+ event.issue_event? &&
7
+ event.event == MERGED_EVENT &&
8
+ merging_transition?(flow)
9
+ end
10
+
11
+ def additive
12
+ true
13
+ end
14
+
15
+ private
16
+
17
+ def merging_transition?(flow)
18
+ flow.transitions(additive).any? do |from, to|
19
+ from.nil? && to.include?(:merged)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Jetmeter
2
+ class OpenAccumulator
3
+ def valid?(resource, flow)
4
+ resource.issue? &&
5
+ opening_transition?(flow) &&
6
+ open_or_finished?(resource)
7
+ end
8
+
9
+ def additive
10
+ true
11
+ end
12
+
13
+ private
14
+
15
+ def opening_transition?(flow)
16
+ if flow
17
+ flow.transitions(additive).any? do |from, to|
18
+ from.nil? && to.include?(:opened)
19
+ end
20
+ end
21
+ end
22
+
23
+ def open_or_finished?(issue)
24
+ issue[:closed_at].nil? || issue.key?(:pull_request)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ module Jetmeter
2
+ class OpenFilter
3
+ def apply?(resource, flow)
4
+ resource.issue? && flow.filters.key?(:open_at) && closed_at?(resource, flow)
5
+ end
6
+
7
+ private
8
+
9
+ def closed_at?(resource, flow)
10
+ resource.closed_at && resource.closed_at < flow.filters[:open_at]
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,13 @@
1
+ module Jetmeter
2
+ class RepositoryIssueEventsLoader
3
+ def initialize(config)
4
+ @repository_name = config.repository_name
5
+ @client = config.client
6
+ end
7
+
8
+ def load
9
+ return @events if defined?(@events)
10
+ @events = @client.repository_issue_events(@repository_name)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Jetmeter
2
+ class RepositoryIssuesLoader
3
+ def initialize(config)
4
+ @repository_name = config.repository_name
5
+ @client = config.client
6
+ end
7
+
8
+ def load
9
+ return @issues if defined?(@issues)
10
+ @issues = @client.list_issues(@repository_name, state: 'all')
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Jetmeter
2
+ class ResourceAdapter < SimpleDelegator
3
+ def issue?
4
+ false
5
+ end
6
+
7
+ def issue_event?
8
+ false
9
+ end
10
+
11
+ def flow_date
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def issue_number
16
+ raise NotImplementedError
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Jetmeter
2
+ VERSION = '0.5.0'.freeze
3
+ end
data/lib/jetmeter.rb ADDED
@@ -0,0 +1,28 @@
1
+ module Jetmeter; end
2
+
3
+ require 'octokit'
4
+ require 'faraday-http-cache'
5
+
6
+ require 'csv'
7
+ require 'delegate'
8
+ require 'io/console'
9
+ require 'fileutils'
10
+
11
+ require 'jetmeter/config'
12
+ require 'jetmeter/config/flow'
13
+ require 'jetmeter/config/client_middleware'
14
+ require 'jetmeter/config/file_cache_store'
15
+ require 'jetmeter/collection'
16
+ require 'jetmeter/repository_issues_loader'
17
+ require 'jetmeter/repository_issue_events_loader'
18
+ require 'jetmeter/issue_adapter'
19
+ require 'jetmeter/issue_event_adapter'
20
+ require 'jetmeter/flow_reducer'
21
+ require 'jetmeter/label_accumulator'
22
+ require 'jetmeter/close_accumulator'
23
+ require 'jetmeter/open_accumulator'
24
+ require 'jetmeter/merge_accumulator'
25
+ require 'jetmeter/date_filter'
26
+ require 'jetmeter/open_filter'
27
+ require 'jetmeter/csv_formatter'
28
+ require 'jetmeter/cli'
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jetmeter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Mark Volosiuk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: octokit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-http-cache
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.10'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 5.10.2
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '5.10'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 5.10.2
75
+ description: Jetmeter is a tool to analyze github repo activity
76
+ email:
77
+ - marchi.martius@gmail.com
78
+ executables:
79
+ - jetmeter
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - bin/jetmeter
84
+ - lib/jetmeter.rb
85
+ - lib/jetmeter/cli.rb
86
+ - lib/jetmeter/close_accumulator.rb
87
+ - lib/jetmeter/collection.rb
88
+ - lib/jetmeter/config.rb
89
+ - lib/jetmeter/config/client_middleware.rb
90
+ - lib/jetmeter/config/file_cache_store.rb
91
+ - lib/jetmeter/config/flow.rb
92
+ - lib/jetmeter/csv_formatter.rb
93
+ - lib/jetmeter/date_filter.rb
94
+ - lib/jetmeter/flow_reducer.rb
95
+ - lib/jetmeter/issue_adapter.rb
96
+ - lib/jetmeter/issue_event_adapter.rb
97
+ - lib/jetmeter/label_accumulator.rb
98
+ - lib/jetmeter/merge_accumulator.rb
99
+ - lib/jetmeter/open_accumulator.rb
100
+ - lib/jetmeter/open_filter.rb
101
+ - lib/jetmeter/repository_issue_events_loader.rb
102
+ - lib/jetmeter/repository_issues_loader.rb
103
+ - lib/jetmeter/resource_adapter.rb
104
+ - lib/jetmeter/version.rb
105
+ homepage:
106
+ licenses:
107
+ - BSD-2-Clause
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.6.11
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Analyze github repo - JT way
129
+ test_files: []