myprecious 0.0.8 → 0.1.2

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.
@@ -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!