myprecious 0.0.8 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,273 @@
1
+ require 'gems'
2
+ require 'myprecious/data_caches'
3
+ require 'pathname'
4
+
5
+ module MyPrecious
6
+ class RubyGemInfo
7
+ include DataCaching
8
+
9
+ MIN_RELEASED_DAYS = 90
10
+ MIN_STABLE_DAYS = 14
11
+
12
+ INFO_CACHE_DIR = MyPrecious.data_cache(DATA_DIR / 'rb-info-cache')
13
+ VERSIONS_CACHE_DIR = MyPrecious.data_cache(DATA_DIR / 'rb-versions-cache')
14
+
15
+ SOURCE_CODE_URI_ENTRIES = %w[github_repo source_code_uri]
16
+
17
+ ##
18
+ # Enumerate Ruby gems used in a project
19
+ #
20
+ # The project at +fpath+ must have a "Gemfile.lock" file as used by the
21
+ # +bundler+ gem.
22
+ #
23
+ # The block receives an Array with three values:
24
+ # - Either +:current+ or +:reqs+, indicating the meaning of the element at
25
+ # index 2,
26
+ # - The name of the gem, and
27
+ # - Either a Gem::Version (if the index-0 element is :current) or a
28
+ # Gem::Requirement (if the index-0 element is :reqs)
29
+ #
30
+ # Iterations yielding +:current+ given the version of the gem currently
31
+ # specified by the Gemfile.lock in the project. Iterations yielding
32
+ # +:reqs+ give requirements on the specified gem dictated by other gems
33
+ # used by the project. Each gem name will appear in only one +:current+
34
+ # iteration, but may occur in multiple +:reqs+ iterations.
35
+ #
36
+ def self.each_gem_used(fpath)
37
+ return enum_for(:each_gem_used, fpath) unless block_given?
38
+
39
+ gemlock = Pathname(fpath).join('Gemfile.lock')
40
+ raise "No Gemfile.lock in #{fpath}" unless gemlock.exist?
41
+
42
+ section = nil
43
+ gemlock.each_line do |l|
44
+ break if l.upcase == l && section == 'GEM'
45
+
46
+ case l
47
+ when /^[A-Z]*\s*$/
48
+ section = l.strip
49
+ when /^\s*(?<gem>\S+)\s+\(\s*(?<gemver>\d[^)]*)\)/
50
+ yield [:current, $~[:gem], Gem::Version.new($~[:gemver])] if section == 'GEM'
51
+ when /^\s*(?<gem>\S+)\s+\(\s*(?<verreqs>[^)]*)\)/
52
+ yield [:reqs, $~[:gem], Gem::Requirement.new(*$~[:verreqs].split(/,\s*/))] if section == 'GEM'
53
+ end
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Build a Hash mapping names of gems used by a project to RubyGemInfo about them
59
+ #
60
+ # The project at +fpath+ must have a "Gemfile.lock" file as used by the
61
+ # +bundler+ gem.
62
+ #
63
+ # The accumulated RubyGemInfo instances should have non-+nil+
64
+ # #current_version values and meaningful information in #version_reqs,
65
+ # as indicated in the Gemfile.lock for +fpath+.
66
+ #
67
+ def self.accum_gem_lock_info(fpath)
68
+ {}.tap do |gems|
69
+ each_gem_used(fpath) do |entry_type, name, verreq|
70
+ g = (gems[name] ||= RubyGemInfo.new(name))
71
+
72
+ case entry_type
73
+ when :current
74
+ g.current_version = verreq
75
+ when :reqs
76
+ g.version_reqs.concat verreq.as_list
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Get an appropriate, human friendly column title for an attribute
84
+ #
85
+ def self.col_title(attr)
86
+ case attr
87
+ when :name then 'Gem'
88
+ else Reporting.common_col_title(attr)
89
+ end
90
+ end
91
+
92
+ def initialize(name)
93
+ super()
94
+ @name = name
95
+ @version_reqs = Gem::Requirement.new
96
+ end
97
+ attr_reader :name, :version_reqs
98
+ attr_accessor :current_version
99
+
100
+ def inspect
101
+ %Q{#<#{self.class.name}:#{'%#.8x' % (object_id << 1)} "#{name}">}
102
+ end
103
+
104
+ def homepage_uri
105
+ get_gems_info['homepage_uri']
106
+ end
107
+
108
+ ##
109
+ # An Array of Arrays containing version (Gem::Version) and release date (Time)
110
+ #
111
+ # The returned Array is sorted in order of descending version number.
112
+ #
113
+ def versions_with_release
114
+ @versions ||= get_gems_versions.map do |ver|
115
+ [
116
+ Gem::Version.new(ver['number']),
117
+ Time.parse(ver['created_at']).freeze
118
+ ].freeze
119
+ end.reject {|vn, rd| vn.prerelease?}.sort.reverse.freeze
120
+ end
121
+
122
+ ##
123
+ # Version number recommended based on stability criteria
124
+ #
125
+ # May return +nil+ if no version meets the established criteria
126
+ #
127
+ def recommended_version
128
+ return nil if versions_with_release.empty?
129
+ return @recommended_version if defined? @recommended_version
130
+
131
+ orig_time_horizon = time_horizon = \
132
+ Time.now - (MIN_RELEASED_DAYS * ONE_DAY)
133
+ horizon_versegs = nonpatch_versegs(versions_with_release[0][0])
134
+
135
+ versions_with_release.each do |ver, released|
136
+ next if ver.prerelease?
137
+ return (@recommended_version = current_version) if current_version && current_version >= ver
138
+
139
+ # Reset the time-horizon clock if moving back into previous patch-series
140
+ if (nonpatch_versegs(ver) <=> horizon_versegs) < 0
141
+ time_horizon = orig_time_horizon
142
+ end
143
+
144
+ if released < time_horizon && version_reqs.satisfied_by?(ver)
145
+ return (@recommended_version = ver)
146
+ end
147
+ time_horizon = [time_horizon, released - (MIN_STABLE_DAYS * ONE_DAY)].min
148
+ end
149
+ return (@recommended_version = nil)
150
+ end
151
+
152
+ def latest_version
153
+ return nil if versions_with_release.empty?
154
+ versions_with_release[0][0]
155
+ end
156
+
157
+ def latest_released
158
+ return nil if versions_with_release.empty?
159
+ versions_with_release[0][1]
160
+ end
161
+
162
+ ##
163
+ # Age in days of the current version
164
+ #
165
+ def age
166
+ return @age if defined? @age
167
+ @age = get_age
168
+ end
169
+
170
+ def license
171
+ gv_data = get_gems_versions
172
+
173
+ curver_data = gv_data.find {|v| Gem::Version.new(v['number']) == current_version}
174
+ current_licenses = curver_data && curver_data['licenses'] || []
175
+
176
+ rcmdd_data = gv_data.find {|v| Gem::Version.new(v['number']) == recommended_version}
177
+ rcmdd_licenses = rcmdd_data && rcmdd_data['licenses'] || current_licenses
178
+
179
+ now_included = rcmdd_licenses - current_licenses
180
+ now_excluded = current_licenses - rcmdd_licenses
181
+
182
+ case
183
+ when now_included.empty? && now_excluded.empty?
184
+ LicenseDescription.new(current_licenses.join(' or '))
185
+ when !now_excluded.empty?
186
+ # "#{current_licenses.join(' or ')} (but rec'd ver. doesn't allow #{now_excluded.join(' or ')})"
187
+ LicenseDescription.new(current_licenses.join(' or ')).tap do |desc|
188
+ desc.update_info = "rec'd ver. doesn't allow #{now_excluded.join(' or ')}"
189
+ end
190
+ when current_licenses.empty? && !now_included.empty?
191
+ LicenseDescription.new("Rec'd ver.: #{now_included.join(' or ')}")
192
+ when !now_included.empty?
193
+ # "#{current_licenses.join(' or ')} (or #{now_included.join(' or ')} on upgrade to rec'd ver.)"
194
+ LicenseDescription.new(current_licenses.join(' or ')).tap do |desc|
195
+ desc.update_info = "or #{now_included.join(' or ')} on upgrade to rec'd ver."
196
+ end
197
+ else
198
+ # "#{current_licenses.join(' or ')} (rec'd ver.: #{rcmdd_licenses.join(' or ')})"
199
+ LicenseDescription.new(current_licenses.join(' or ')).tap do |desc|
200
+ desc.update_info = "rec'd ver.: #{rcmdd_licenses.join(' or ')}"
201
+ end
202
+ end
203
+ end
204
+
205
+ def changelogs
206
+ gv_data = get_gems_versions.sort_by {|v| Gem::Version.new(v['number'])}.reverse
207
+ if current_version
208
+ gv_data = gv_data.take_while {|v| Gem::Version.new(v['number']) > current_version}
209
+ end
210
+ gv_data.collect {|v| (v['metadata'] || {})['changelog_uri']}.compact.uniq
211
+ end
212
+
213
+ def changelog
214
+ changelogs[0]
215
+ end
216
+
217
+ def days_between_current_and_recommended
218
+ v, cv_rel = versions_with_release.find {|v, r| v == current_version} || []
219
+ v, rv_rel = versions_with_release.find {|v, r| v == recommended_version} || []
220
+ return nil if cv_rel.nil? || rv_rel.nil?
221
+
222
+ return ((rv_rel - cv_rel) / ONE_DAY).to_i
223
+ end
224
+
225
+ def obsolescence
226
+ cv_major = current_version && current_version.segments[0]
227
+ rv_major = recommended_version && recommended_version.segments[0]
228
+ at_least_moderate = false
229
+ case
230
+ when cv_major.nil? || rv_major.nil?
231
+ # Can't compare
232
+ when cv_major + 1 < rv_major
233
+ # More than a single major version difference is severe
234
+ return :severe
235
+ when cv_major < rv_major
236
+ # Moderate obsolescence if we're a major version behind
237
+ at_least_moderate = true
238
+ end
239
+
240
+ days_between = days_between_current_and_recommended
241
+
242
+ return Reporting.obsolescence_by_age(days_between, at_least_moderate: at_least_moderate)
243
+ end
244
+
245
+ def source_code_uri
246
+ metadata = get_gems_info['metadata']
247
+ SOURCE_CODE_URI_ENTRIES.each {|k| return metadata[k] if metadata[k]}
248
+ return nil
249
+ end
250
+
251
+ private
252
+ def get_gems_info
253
+ cache = INFO_CACHE_DIR.join("#{name}.json")
254
+ apply_cache(cache) {Gems.info(name)}
255
+ end
256
+
257
+ def get_gems_versions
258
+ cache = VERSIONS_CACHE_DIR.join("#{name}.json")
259
+ apply_cache(cache) {Gems.versions(name)}
260
+ end
261
+
262
+ def get_age
263
+ versions_with_release.each do |ver, released|
264
+ return ((Time.now - released) / ONE_DAY).to_i if ver == current_version
265
+ end
266
+ return nil
267
+ end
268
+
269
+ def nonpatch_versegs(v)
270
+ v.segments[0..-2]
271
+ end
272
+ end
273
+ end
metadata CHANGED
@@ -1,56 +1,175 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: myprecious
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Balki Kodarapu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-28 00:00:00.000000000 Z
11
+ date: 2020-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gems
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.0.0
20
17
  - - "~>"
21
18
  - !ruby/object:Gem::Version
22
19
  version: '1.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 1.0.0
30
27
  - - "~>"
31
28
  - !ruby/object:Gem::Version
32
29
  version: '1.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: git
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.5'
37
40
  - - ">="
38
41
  - !ruby/object:Gem::Version
39
42
  version: 1.5.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
40
47
  - - "~>"
41
48
  - !ruby/object:Gem::Version
42
49
  version: '1.5'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.5.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: rake-toolkit_program
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
43
60
  type: :runtime
44
61
  prerelease: false
45
62
  version_requirements: !ruby/object:Gem::Requirement
46
63
  requirements:
47
64
  - - ">="
48
65
  - !ruby/object:Gem::Version
49
- version: 1.5.0
66
+ version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rest-client
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
50
71
  - - "~>"
51
72
  - !ruby/object:Gem::Version
52
- version: '1.5'
53
- description: A simple, markdown generated with information about your gems
73
+ version: 2.0.2
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '2.0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 2.0.2
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '2.0'
87
+ - !ruby/object:Gem::Dependency
88
+ name: parslet
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '2.0'
94
+ type: :runtime
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '2.0'
101
+ - !ruby/object:Gem::Dependency
102
+ name: rubyzip
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '2.3'
108
+ type: :runtime
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - "~>"
113
+ - !ruby/object:Gem::Version
114
+ version: '2.3'
115
+ - !ruby/object:Gem::Dependency
116
+ name: bundler
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: '1.13'
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '1.13'
129
+ - !ruby/object:Gem::Dependency
130
+ name: pry
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '0.13'
136
+ type: :development
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '0.13'
143
+ - !ruby/object:Gem::Dependency
144
+ name: rb-readline
145
+ requirement: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: '0.5'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - "~>"
155
+ - !ruby/object:Gem::Version
156
+ version: '0.5'
157
+ - !ruby/object:Gem::Dependency
158
+ name: byebug
159
+ requirement: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ type: :development
165
+ prerelease: false
166
+ version_requirements: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ description: A simple, markdown generated with information about your gems and python
172
+ packages
54
173
  email: balki.kodarapu@gmail.com
55
174
  executables:
56
175
  - myprecious
@@ -59,6 +178,9 @@ extra_rdoc_files: []
59
178
  files:
60
179
  - bin/myprecious
61
180
  - lib/myprecious.rb
181
+ - lib/myprecious/data_caches.rb
182
+ - lib/myprecious/python_packages.rb
183
+ - lib/myprecious/ruby_gems.rb
62
184
  homepage: http://rubygems.org/gems/myprecious
63
185
  licenses:
64
186
  - MIT
@@ -78,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
200
  - !ruby/object:Gem::Version
79
201
  version: '0'
80
202
  requirements: []
81
- rubygems_version: 3.0.3
203
+ rubygems_version: 3.1.2
82
204
  signing_key:
83
205
  specification_version: 4
84
206
  summary: Your precious dependencies!