any_good 0.0.1

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: 2fe801250f6e51612d255456a8ad2d7e7cea083b
4
+ data.tar.gz: b8db3eb675a42e1ca7bc9f9ece8e70ddd275da63
5
+ SHA512:
6
+ metadata.gz: 985c456c6133c922c904176719795fc1fc737deba31b90fe33c5c514fb5e504f8015019b39452638c2f21283e78375055c2b129d087f5284eb00ef9b1b213f51
7
+ data.tar.gz: d457b7e29bf41e278beac08b949c9c473d9e917f879548ffe063ab04b31430d493563c91492475c24426a9c09d07bdf355ccf4ec0f2556d87632fca1874d9854
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014-15 Victor 'Zverok' Shepelev
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,61 @@
1
+ [![Gem Version](https://badge.fury.io/rb/any_good.svg)](http://badge.fury.io/rb/any_good)
2
+
3
+ ## What's this?
4
+
5
+ A thing to quickly evaluate Ruby gem maturity and answer "Is it any good?". Like this:
6
+
7
+ ![](https://raw.githubusercontent.com/zverok/any_good/master/doc/example.png)
8
+
9
+ Just a report of some numbers and facts from rubygems.org and GitHub repo of the gem in
10
+ question, to understand how risky it would be to use it.
11
+
12
+ ## Usage
13
+
14
+ ```
15
+ $ gem install any_good
16
+ $ any_good <gem_name>
17
+ ```
18
+
19
+ ## Why?
20
+
21
+ I find myself constantly repeating this process for some new gems I spotted somewhere: going to
22
+ their gem page and repo page to understand how well it maintained or is it abandoned, and is it
23
+ something new that still have to get its ways and popularity.
24
+
25
+ This gem is just a quick one-evening experiment on whether it can be automated in a helpful manner.
26
+
27
+ ## What are the colors? Are you criticizing gems?..
28
+
29
+ The colors (green-yellow-red) are just based on my own subjective "thresholds". Maybe they could become
30
+ configurable in future versions, if any. "Yellow" and "red" aren't, in fact, "bad", it is "point
31
+ of attention" when judging whether you'll give a chance to some gem, and how important are they,
32
+ depends on the situation.
33
+
34
+ The colors DO NOT mean to "score" the gems (this gem is bad) or to compare "which gem is better",
35
+ but the typically DO give some insights on the gem's current status in the community.
36
+
37
+ Those insights aren't that accurate: for example, [tzinfo](https://rubygems.org/gems/tzinfo)
38
+ is used by literally everyone, yet its GitHub repo has just ~200 stars. For another,
39
+ [inflecto](https://rubygems.org/gems/inflecto) is explicitly abandoned by its author, the last version
40
+ was released four years ago, yet it is robust and widely used.
41
+
42
+ ## GitHub?
43
+
44
+ A large part of stats is taken from gem's GitHub repo.
45
+
46
+ Yes, the gem is not required to have the link to sources published. Yes, there are gems with sources
47
+ on GitLab, BitBucket, or even SourceForge, God forbid. But again, as with colors, my _subjective_
48
+ experience says me to check its GitHub if it is accessible. So the `any_good` does.
49
+
50
+ BTW, `any_good` connects to GitHub API anonymously, and anonymous connections are subject to harsh
51
+ rate limiting, so if you use it a lot through one day, you may want to provide `GITHUB_ACCESS_TOKEN`
52
+ environment variable (tokens are obtained [here](https://github.com/settings/tokens)).
53
+
54
+ ## Is it any good?
55
+
56
+ Well, it is a quick one-evening experiment. So, no tests, no docs except this README, no config,
57
+ and just hard-coded thresholds. But it works for me.
58
+
59
+ ## Who are you anyways?
60
+
61
+ Just a humble [@zverok](http://zverok.github.io).
data/exe/any_good ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/any_good'
3
+
4
+ abort 'Usage: `any_good <gemname>`' if ARGV.empty?
5
+
6
+ AnyGood.new(ARGV.shift).fetch.report
@@ -0,0 +1,73 @@
1
+ class AnyGood
2
+ class Meter < Struct.new(:name, :thresholds, :block)
3
+ def call(data)
4
+ Metric.new(name, value(data), *thresholds)
5
+ end
6
+
7
+ private
8
+
9
+ attr_reader :data
10
+
11
+ def value(data)
12
+ @data = data
13
+ instance_eval(&block)
14
+ rescue NoMethodError
15
+ nil
16
+ ensure
17
+ @data = nil
18
+ end
19
+ end
20
+
21
+ class Metric
22
+ attr_reader :value, :name, :color
23
+
24
+ def initialize(name, value, *thresholds)
25
+ @name = name
26
+ @value = value
27
+ @color = deduce_color(*thresholds)
28
+ end
29
+
30
+ def format
31
+ '%20s: %s' % [name, colorized_value]
32
+ end
33
+
34
+ private
35
+
36
+ def colorized_value
37
+ Pastel.new.send(color, formatted_value)
38
+ end
39
+
40
+ def formatted_value
41
+ case value
42
+ when nil
43
+ '—'
44
+ when String
45
+ value
46
+ when Numeric
47
+ value.to_s.chars.reverse.each_slice(3).to_a.map(&:join).join(',').reverse
48
+ when Date, Time
49
+ diff = TimeMath.measure(value, Time.now)
50
+ unit, num = diff.detect { |_, v| !v.zero? }
51
+ "#{num} #{unit} ago"
52
+ else
53
+ fail ArgumentError, "Unformattable #{value.inspect}"
54
+ end
55
+ end
56
+
57
+ def deduce_color(yellow = nil, red = nil)
58
+ return :dark if value.nil?
59
+ return :white if !yellow # no thresholds given
60
+
61
+ # special trick to tell "lower is better" from "higher is better" situations
62
+ val = red.is_a?(Numeric) && red < 0 ? -value : value
63
+
64
+ if val < red
65
+ :red
66
+ elsif val < yellow
67
+ :yellow
68
+ else
69
+ :green
70
+ end
71
+ end
72
+ end
73
+ end
data/lib/any_good.rb ADDED
@@ -0,0 +1,94 @@
1
+ require 'gems'
2
+ require 'octokit'
3
+ require 'pp'
4
+ require 'date'
5
+ require 'time_math'
6
+ require 'pastel'
7
+
8
+ class AnyGood
9
+ attr_reader :name
10
+
11
+ def initialize(name)
12
+ @name = name
13
+ @github_client =
14
+ if (token = ENV['GITHUB_ACCESS_TOKEN'])
15
+ Octokit::Client.new(access_token: token).tap { |client| client.user.login }
16
+ else
17
+ Octokit::Client.new
18
+ end
19
+ end
20
+
21
+ def fetch
22
+ data = {
23
+ gem: Gems.info(name),
24
+ gem_versions: Gems.versions(name),
25
+ gem_rev_deps: Gems.reverse_dependencies(name)
26
+ }
27
+
28
+ repo_id = [data[:gem]['source_code_uri'], data[:gem]['homepage_uri']]
29
+ .grep(%r{^https?://github\.com/}).first&.sub(%r{^https?://github\.com/}, '')
30
+
31
+ if repo_id
32
+ data.merge!(
33
+ repo: @github_client.repository(repo_id).to_h,
34
+ open_issues: @github_client.issues(repo_id, state: 'open', per_page: 50).map(&:to_h),
35
+ closed_issues: @github_client.issues(repo_id, state: 'closed', per_page: 50).map(&:to_h),
36
+ last_commit: @github_client.commits(repo_id, per_page: 1).first.to_h
37
+ # open_prs: @github_client.issues(repo_id, state: 'open').map(&:to_h)
38
+ # closed_prs: @github_client.issues(repo_id, state: 'closed').map(&:to_h)
39
+ )
40
+ end
41
+
42
+ @data = OpenStruct.new(data)
43
+
44
+ self
45
+ end
46
+
47
+ def report
48
+ self.class.meters.each do |meter|
49
+ puts meter.call(@data).format
50
+ end
51
+ self
52
+ end
53
+
54
+ require_relative 'any_good/meters'
55
+
56
+ def self.meters
57
+ @meters ||= []
58
+ end
59
+
60
+ def self.metric(name, thresholds: nil, &block)
61
+ meters << Meter.new(name, thresholds, block)
62
+ end
63
+
64
+ T = TimeMath
65
+ now = Time.now
66
+
67
+ metric('Downloads', thresholds: [5_000, 10_000]) { data.gem['downloads'] }
68
+ metric('Latest version', thresholds: [T.month.decrease(now, 2), T.year.decrease(now)]) {
69
+ Time.parse(data.gem_versions.first['created_at'])
70
+ }
71
+ metric('Used by', thresholds: [10, 100]) { data.gem_rev_deps.count }
72
+
73
+ metric('Stars', thresholds: [100, 500]) { data.repo[:stargazers_count] }
74
+ metric('Forks', thresholds: [5, 20]) { data.repo[:forks_count] }
75
+ metric('Last commit', thresholds: [T.month.decrease(now), T.month.decrease(now, 4)]) {
76
+ data.last_commit.dig(:commit, :committer, :date)
77
+ }
78
+
79
+ metric('Open issues') {
80
+ res = data.open_issues.count
81
+ res == 50 ? '50+' : res
82
+ }
83
+ metric('...without reaction', thresholds: [-5, -20]) {
84
+ data.open_issues.reject { |i| i[:labels].any? || i[:comments] > 0 }.count
85
+ }
86
+ metric('...last reaction', thresholds: [T.week.decrease(now), T.month.decrease(now)]) {
87
+ data.open_issues.detect { |i| i[:labels].any? || i[:comments] > 0 }&.fetch(:updated_at)
88
+ }
89
+ metric('Closed issues') {
90
+ res = data.closed_issues.count
91
+ res == 50 ? '50+' : res
92
+ }
93
+ metric('...last closed') { data.closed_issues.first&.fetch(:closed_at) }
94
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: any_good
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Victor Shepelev
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pastel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: octokit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: gems
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: time_math2
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubygems-tasks
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description:
98
+ email: zverok.offline@gmail.com
99
+ executables:
100
+ - any_good
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - LICENSE.txt
105
+ - README.md
106
+ - exe/any_good
107
+ - lib/any_good.rb
108
+ - lib/any_good/meters.rb
109
+ homepage: https://github.com/zverok/any_good
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 2.3.0
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.6.10
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: Is that gem any good?
133
+ test_files: []