gemverse 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 69db10da33808ff62ca728d7ada452de1283a465d9fbbce8b9919d88f976a9b4
4
+ data.tar.gz: 1554b1c90f9c0e467bee55f2e98eff8ffae13e3b2090d8bd5bb21ab86b81b645
5
+ SHA512:
6
+ metadata.gz: 4d640f9f26100ae1646431670fa275bf267bb9ab88727d754e0c3efffaa29c74c2ea1136da0516908de48020aead49e38aed93780a917c85c04361e84ac3b518
7
+ data.tar.gz: 5fbf83a842f797c19b8054aa1b86c3891650bf5a1a6e9bb85dda0371755713fe38d0321db4c653a70951f72e64c5017eea6e831158d4ac1b6c2fdfdf4346ef38
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.0.1 / 2023-01-20
2
+
3
+ * Everything is new. First release.
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/gemverse.rb
6
+ lib/gemverse/api.rb
7
+ lib/gemverse/cache.rb
8
+ lib/gemverse/gems.rb
9
+ lib/gemverse/timeline.rb
data/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # Gemverse - Gem Universe
2
+
3
+ gemverse gem - gem universe incl. rubygems API V1 wrapper lite; gem version cache, gem timeline reports, 'n' more
4
+
5
+
6
+
7
+ * home :: [github.com/rubycocos/git](https://github.com/rubycocos/git)
8
+ * bugs :: [github.com/rubycocos/git/issues](https://github.com/rubycocos/git/issues)
9
+ * gem :: [rubygems.org/gems/gemverse](https://rubygems.org/gems/gemverse)
10
+ * rdoc :: [rubydoc.info/gems/gemverse](http://rubydoc.info/gems/gemverse)
11
+
12
+
13
+
14
+
15
+
16
+ ## Usage
17
+
18
+
19
+ ### RubyGems API "To The Metal" Wrapper V1 - Lite Edition
20
+
21
+ The gemverse includes a lightweight "to the metal"
22
+ wrapper for the rubygems API V1
23
+ that returns data(sets) in the JSON format:
24
+
25
+ ``` ruby
26
+ require 'gemverse'
27
+
28
+ ## get all gems owned by the author with the handle / known as gettalong
29
+ data = Gems::API.gems_by( 'gettalong' )
30
+ # same as https://rubygems.org/api/v1/owners/gettalong/gems.json
31
+
32
+ ## get all versions of the hexapdf gem
33
+ data = Gems::API.versions( 'hexapdf' )
34
+ # same as https://rubygems.org/api/v1/versions/hexpdf.json"
35
+
36
+ #...
37
+ ```
38
+
39
+
40
+ ### Gem Cache 'n' Timeline Reports
41
+
42
+ Let's build a gem timeline report / what's news page.
43
+ Let's spotlight the work of [Thomas Leitner, Austria also known as `gettalong`](https://rubygems.org/profiles/gettalong)
44
+ who 24 published gems (as of 2023) in the last 10+ years.
45
+
46
+ Note: Yes, you can. Replace the `gettalong` rubygems id / login with your own to build your very own timeline.
47
+
48
+
49
+ **Step 1 - Online - Get gems & versions via "higher-level" rubygems api calls**
50
+
51
+ ``` ruby
52
+ cache = Gems::Cache.new( './cache' )
53
+
54
+ gems = Gems.find_by( owner: 'gettalong' )
55
+ puts " #{gems.size} record(s)"
56
+
57
+ ## bonus: save gems in a "flat" tabular datafile using the comma-separated values (.csv) format
58
+ gems.export( './profile/gettalong/gems.csv' )
59
+
60
+ ## fetch all gem versions and (auto-save)
61
+ ## in a "flat" tabular datafile (e.g. <gem>/versions.csv)
62
+ ## using the comma-spearated values (.csv) format
63
+ ## in the cache directory
64
+ cache.update_versions( gems: gems )
65
+ ```
66
+
67
+
68
+ **Step 2 - Offline - Read versions from cache and build reports / timeline**
69
+
70
+ ``` ruby
71
+ cache = Gems::Cache.new( './gems' )
72
+
73
+ gems = read_csv( './profile/gettalong/gems.csv' )
74
+ puts " #{gems.size} record(s)"
75
+
76
+ versions = cache.read_versions( gems: gems )
77
+ puts " #{versions.size} record(s)"
78
+
79
+ timeline = Gems::Timeline.new( versions,
80
+ title: "Thomas Leitner's Timeline" )
81
+ timeline.save( "./profile/gettalong/README.md" )
82
+ ```
83
+
84
+
85
+ That's it.
86
+
87
+
88
+
89
+ Tip: You can build "custom" timeline reports
90
+ and filter / select the gems to include as you like.
91
+ Let's (re)build the timeline for all ruby cocos (code commons)
92
+ gems.
93
+
94
+ ``` ruby
95
+ cache = Gems::Cache.new( './cache' )
96
+
97
+ gems = read_csv( './collection/cocos.csv' )
98
+ puts " #{gems.size} record(s)"
99
+
100
+ versions = cache.read_versions( gems: gems )
101
+ puts " #{versions.size} record(s)"
102
+
103
+ timeline = Gems::Timeline.new( versions,
104
+ title: 'Ruby Code Commons (COCOS) Timeline' )
105
+ timeline.save( "./collection/cocos/README.md" )
106
+ ```
107
+
108
+ That's it.
109
+
110
+
111
+ See
112
+ [Thomas Leitner's Timeline](samples/gems_gettalong),
113
+ [Jan Lelis's Timeline](samples/gems_janlelis),
114
+ [Ruby Code Commons (COCOS) Timeline](samples/gems_cocos), and some more
115
+ for some real-world timeline samples.
116
+
117
+
118
+
119
+ ## License
120
+
121
+ The `gemverse` scripts are dedicated to the public domain.
122
+ Use it as you please with no restrictions whatsoever.
123
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'hoe'
2
+
3
+ Hoe.spec 'gemverse' do
4
+
5
+ self.version = '0.0.1' # note: for now add version inline
6
+
7
+ self.summary = "gemverse gem - gem universe incl. rubygems API V1 wrapper lite; gem version cache, gem timeline reports, 'n' more"
8
+ self.description = summary
9
+
10
+ self.urls = { home: 'https://github.com/rubycocos/git' }
11
+
12
+ self.author = 'Gerald Bauer'
13
+ self.email = 'opensport@googlegroups.com'
14
+
15
+ # switch extension to .markdown for gihub formatting
16
+ self.readme_file = 'README.md'
17
+ self.history_file = 'CHANGELOG.md'
18
+
19
+ self.licenses = ['Public Domain']
20
+
21
+ self.extra_deps = [
22
+ ['cocos'],
23
+ ]
24
+
25
+ self.spec_extras = {
26
+ required_ruby_version: '>= 2.2.2'
27
+ }
28
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module Gems
3
+ module API
4
+
5
+ BASE = 'https://rubygems.org/api/v1'
6
+
7
+ def self.gems_by( owner )
8
+ src = "#{BASE}/owners/#{owner}/gems.json"
9
+ call( src )
10
+ end
11
+
12
+
13
+ def self.versions( name )
14
+ ## note: will NOT include yanked versions
15
+ ## check if there's a query parameter ???
16
+ src = "#{BASE}/versions/#{name}.json"
17
+ call( src )
18
+ end
19
+
20
+
21
+ def self.call( src ) ## get response as (parsed) json (hash table)
22
+
23
+ headers = {
24
+ # 'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
25
+ 'User-Agent' => "ruby v#{RUBY_VERSION}",
26
+ }
27
+
28
+ response = Webclient.get( src, headers: headers )
29
+
30
+ if response.status.ok?
31
+ puts "#{response.status.code} #{response.status.message} - content_type: #{response.content_type}, content_length: #{response.content_length}"
32
+
33
+ response.json
34
+ else
35
+ puts "!! HTTP ERROR:"
36
+ puts "#{response.status.code} #{response.status.message}"
37
+ exit 1
38
+ end
39
+ end
40
+
41
+ end # module API
42
+ end # module Gems
@@ -0,0 +1,100 @@
1
+
2
+
3
+ module Gems
4
+ class Cache
5
+ def initialize( basedir='./gems' )
6
+ @basedir = basedir
7
+ end
8
+
9
+
10
+ def update_versions( gems: [] )
11
+
12
+ delay_in_s = 0.5
13
+
14
+ gems.each_with_index do |gem,i|
15
+
16
+ name = if gem.is_a?( String )
17
+ gem
18
+ elsif gem.is_a?( Hash )
19
+ gem['name']
20
+ else ## assume our own Gem struct/class for now
21
+ gem.name
22
+ end
23
+
24
+ puts "==> #{i+1}/#{gems.size} #{name}..."
25
+
26
+ ## update versions info
27
+ puts " sleeping #{delay_in_s} second(s)"
28
+ sleep( delay_in_s )
29
+
30
+ data = Gems::API.versions( name )
31
+ puts " #{data.size} version record(s) found"
32
+
33
+ headers = ['name',
34
+ 'version',
35
+ 'created',
36
+ 'downloads']
37
+ recs = []
38
+ data.each do |h|
39
+ ## only use year/month/day for now now hours etc.
40
+ created = Date.strptime( h['created_at'], '%Y-%m-%d' )
41
+
42
+ rec = [
43
+ name,
44
+ h['number'],
45
+ created.strftime( '%Y-%m-%d' ),
46
+ h['downloads_count'].to_s,
47
+ ]
48
+ recs << rec
49
+ end
50
+ write_csv( "#{@basedir}/#{name}/versions.csv", [headers]+recs )
51
+ end
52
+ end
53
+
54
+
55
+
56
+ def read_versions( gems: [] )
57
+ ## read in and merge all version records
58
+ versions = []
59
+
60
+ ## if no gems passed in - get all versions.csv datasets in basedir
61
+ if gems.empty?
62
+ paths = Dir.glob( "#{@basedir}/*/versions.csv" )
63
+ gems = paths.map { |path| File.basename(File.dirname(path)) }
64
+ end
65
+
66
+ gems.each_with_index do |gem,i|
67
+
68
+ name = if gem.is_a?( String )
69
+ gem
70
+ elsif gem.is_a?( Hash )
71
+ gem['name']
72
+ else ## assume our own Gem struct/class for now
73
+ gem.name
74
+ end
75
+
76
+ path = "#{@basedir}/#{name}/versions.csv"
77
+ puts "==> #{i+1}/#{gems.size} reading #{name}..."
78
+ recs = read_csv( path )
79
+ recs.reverse.each_with_index do |rec,n|
80
+ more = { 'count' => (n+1).to_s } ## auto-add version count(er) e.g. 1,2,3,...
81
+ versions << rec.merge( more )
82
+ end
83
+ puts " #{recs.size} record(s)"
84
+ end
85
+
86
+ ## sort by version created and name
87
+ versions = versions.sort do |l,r|
88
+ res = r['created'] <=> l['created']
89
+ res = l['name'] <=> r['name'] if res == 0
90
+ res = r['version'] <=> l['version'] if res == 0
91
+ res
92
+ end
93
+
94
+ versions
95
+ end
96
+
97
+
98
+
99
+ end ## class Cache
100
+ end ## module Gems
@@ -0,0 +1,105 @@
1
+
2
+ module Gems
3
+
4
+
5
+
6
+ class GemDataset ## rename to Gems or Gemset or such - why? why not?
7
+
8
+
9
+ def initialize( gems )
10
+ @gems = gems
11
+ end
12
+
13
+ def size() @gems.size; end
14
+ def each_with_index( &block ) @gems.each_with_index( &block ); end
15
+ def each( &block ) @gems.each( &block ); end
16
+
17
+
18
+ ## todo/check: add an write_csv( path ) alias / alternate method name - why? why not?
19
+ def export( path )
20
+ recs = []
21
+
22
+ @gems.each do |gem|
23
+
24
+ if gem.yanked?
25
+ puts "!! ERROR - includes yanked gem"
26
+ pp gem
27
+ exit 1
28
+ end
29
+
30
+ rec = [gem.name,
31
+ gem.version,
32
+ gem.version_created.strftime( '%Y-%m-%d' ),
33
+ gem.version_downloads.to_s,
34
+ gem.homepage,
35
+ gem.runtime_dependencies.join( ' | ' ),
36
+ ]
37
+ recs << rec
38
+ end
39
+ headers = ['name',
40
+ 'version',
41
+ 'version_created',
42
+ 'version_downloads',
43
+ 'homepage',
44
+ 'dependencies',
45
+ ]
46
+ write_csv( path, [headers]+recs )
47
+ end # method export
48
+ end ## class GemDataset
49
+
50
+
51
+
52
+
53
+ class Gem ## todo/check: rename or use Gem::Meta or such - why? why not?
54
+
55
+ attr_reader :name,
56
+ :version, :version_created, :version_downloads,
57
+ :homepage,
58
+ :runtime
59
+
60
+
61
+ def self.create( h ) ## todo/check: rename to create_from_json or such - why? why not?
62
+ ## optional keyword args
63
+ kwargs = {
64
+ version: h['version'],
65
+ ## only use year/month/day for now now hours etc. - why? why not
66
+ version_created: h['version_created_at'] ? Date.strptime( h['version_created_at'], '%Y-%m-%d' ) : nil,
67
+ version_downloads: h['version_downloads'],
68
+ ## note: (auto-)clean
69
+ ## for example - newline seen in "http://icanhasaudio.com/\n" !!!
70
+ homepage: h['homepage_uri'] ? h['homepage_uri'].gsub( /[ \r\n]/, '')
71
+ : nil,
72
+ yanked: h['yanked'],
73
+ runtime: h['dependencies']['runtime'].map { |dep| dep['name'] }
74
+ }
75
+
76
+ new( name: h['name'],
77
+ **kwargs )
78
+ end
79
+
80
+
81
+ def initialize( name:,
82
+ version: nil,
83
+ version_created: nil,
84
+ version_downloads: nil,
85
+ homepage: nil,
86
+ yanked: nil,
87
+ runtime: [] )
88
+
89
+ @name = name
90
+ @version = version
91
+ @version_created = version_created
92
+ @version_downloads = version_downloads
93
+ @homepage = homepage
94
+ @yanked = yanked
95
+ @runtime = runtime
96
+ end
97
+
98
+ alias_method :runtime_dependencies, :runtime
99
+
100
+ ## always return false if not defined - why? why not?
101
+ def yanked?() @yanked.nil? ? false : @yanked; end
102
+ end # class Gems
103
+
104
+
105
+ end ## module Gems
@@ -0,0 +1,161 @@
1
+
2
+ module Gems
3
+ class Timeline
4
+
5
+ def initialize( versions )
6
+ ## sort by version created and name
7
+ @versions = versions.sort do |l,r|
8
+ res = r['created'] <=> l['created']
9
+ res = l['name'] <=> r['name'] if res == 0
10
+ res = r['version'] <=> l['version'] if res == 0
11
+ res
12
+ end
13
+ end
14
+
15
+
16
+ def save( path )
17
+ buf = build
18
+ write_text( path, buf )
19
+ end
20
+
21
+
22
+ def build
23
+ ## step 1 - build document model - versions by year & week
24
+ ## and split into new & update
25
+ model = {}
26
+
27
+ @versions.each do |rec|
28
+
29
+ date = Date.strptime( rec['created'], '%Y-%m-%d' )
30
+ year = date.year.to_s
31
+ week = date.strftime('%V') ## note: return zero-padded string e.g. 01, 02, etc.
32
+
33
+ by_year = model[ year ] ||= {}
34
+ by_week = by_year[ week ] ||= { 'new' => [],
35
+ 'updated' => [] }
36
+
37
+ if rec['count'].to_i == 1
38
+ by_week[ 'new' ] << rec
39
+ else
40
+ by_week[ 'updated' ] << rec
41
+ end
42
+ end
43
+
44
+ ## pp model
45
+
46
+ ## step 2 - build document
47
+
48
+ buf = String.new
49
+ buf << "# Timeline \n\n"
50
+
51
+ ## add breadcrumps for years
52
+ buf << model.keys.map do |year|
53
+ "[#{year}](##{year})"
54
+ end.join( ' · ' )
55
+ buf << "\n\n"
56
+
57
+ ## add new gems by year
58
+ buf << "## New Gems By Year\n\n"
59
+
60
+ model.each do |year, by_week|
61
+ ## get totals for year
62
+ gems_new = by_week.values.reduce( [] ) do |acc,gems|
63
+ acc + gems['new']
64
+ end
65
+
66
+ buf << "**#{year}** - "
67
+
68
+ buf << if gems_new.size > 0
69
+ _build_gems_new( gems_new )
70
+ else
71
+ "Ø \n\n"
72
+ end
73
+ end
74
+
75
+
76
+ model.each do |year, by_week|
77
+ buf << "## #{year}\n\n"
78
+
79
+ by_week.each do |week, gems|
80
+ buf << "**Week #{week}**\n\n"
81
+
82
+ buf << _build_gems_new( gems['new'] )
83
+ buf << _build_gems_updated( gems['updated'] )
84
+ end
85
+ end
86
+
87
+ buf
88
+ end # method build
89
+
90
+
91
+ def _build_gem( gem )
92
+ date = Date.strptime( gem['created'], '%Y-%m-%d' )
93
+
94
+ buf = String.new
95
+ buf << "[**"
96
+ buf << gem['name']
97
+ buf << "**]"
98
+ buf << "("
99
+ buf << "https://rubygems.org/gems/#{gem['name']}/versions/#{gem['version']}"
100
+ buf << " "
101
+ buf << %Q{"update no.#{gem['count']} @ #{date.strftime('%a %d %b %Y')}"}
102
+ buf << ") "
103
+ buf << gem['version']
104
+ buf
105
+ end
106
+
107
+
108
+ def _build_gems_new( gems )
109
+ buf = String.new
110
+ if gems.size > 0
111
+ buf << "**NEW!** - #{gems.size} Gem(s) - "
112
+ buf << gems.map do |gem| _build_gem( gem )
113
+ end.join( ', ' )
114
+ buf << "\n\n"
115
+ end
116
+ buf
117
+ end
118
+
119
+ def _build_gems_updated( gems )
120
+ buf = String.new
121
+ if gems.size > 0
122
+ buf << "#{gems.size} Update(s) - "
123
+ buf << gems.map do |gem| _build_gem( gem )
124
+ end.join( ', ' )
125
+ buf << "\n\n"
126
+ end
127
+ buf
128
+ end
129
+
130
+
131
+
132
+
133
+
134
+ def export( path )
135
+
136
+ headers = ['week',
137
+ 'name',
138
+ 'version',
139
+ 'count',
140
+ 'created',
141
+ 'downloads']
142
+ recs = []
143
+ @versions.each_with_index do |h,i|
144
+
145
+ date = Date.strptime( h['created'], '%Y-%m-%d' )
146
+
147
+ rec = [
148
+ date.strftime('%Y/%V'),
149
+ h['name'],
150
+ h['version'],
151
+ h['count'],
152
+ date.strftime( '%Y-%m-%d' ), # add (%a) for (Sun), (Mon) or such - why? why not?
153
+ h['downloads'],
154
+ ]
155
+ recs << rec
156
+ end
157
+
158
+ write_csv( path, [headers]+recs )
159
+ end
160
+ end ## class Timeline
161
+ end ## module Gems
data/lib/gemverse.rb ADDED
@@ -0,0 +1,95 @@
1
+ require 'cocos'
2
+
3
+
4
+ ## move to cocos - upstream - why? why not?
5
+ def write_csv( path, recs )
6
+ buf = String.new
7
+ buf << recs[0].join( ', ' )
8
+ buf << "\n"
9
+ recs[1..-1].each do |values|
10
+ buf << values.join( ', ' )
11
+ buf << "\n"
12
+ end
13
+ write_text( path, buf )
14
+ end
15
+
16
+
17
+
18
+
19
+
20
+
21
+ require_relative 'gemverse/api'
22
+ require_relative 'gemverse/gems'
23
+ require_relative 'gemverse/cache'
24
+
25
+ require_relative 'gemverse/timeline' ## timeline report
26
+
27
+
28
+
29
+ module Gems
30
+
31
+ ###
32
+ ## "high-level" finders
33
+
34
+ def self.find_by( owner: ) ## todo/check: use
35
+
36
+ rows = API.gems_by( owner )
37
+ # pp data
38
+ puts " #{rows.size} record(s) founds"
39
+
40
+ ## write "raw respone" to cache for debugging
41
+ write_json( "./cache/gems_#{owner}.json", rows )
42
+
43
+
44
+
45
+ recs = []
46
+ rows.each do |row|
47
+ recs << Gem.create( row )
48
+ end
49
+
50
+ ## sort by
51
+ ## 1) (latest) version created and
52
+ ## 2) name
53
+ recs = recs.sort do |l,r|
54
+ res = r.version_created <=> l.version_created
55
+ res = l.name <=> r.name if res == 0
56
+ res
57
+ end
58
+
59
+ GemDataset.new( recs )
60
+ end
61
+
62
+
63
+
64
+ def self.read_csv( path )
65
+ ## note: requires Kernel:: otherwise stackoverflow endlessly calling read_csv
66
+ rows = Kernel::read_csv( path )
67
+ puts " #{rows.size} record(s) founds"
68
+
69
+ recs = []
70
+ rows.each do |row|
71
+ kwargs = {
72
+ version: row['version'].empty? ? nil : row['version'],
73
+ version_downloads: row['version_downloads'].empty? ? nil : row['version_downloads'].to_i,
74
+ version_created: row['version_created'].empty? ? nil : Date.strptime( row['version_created'], '%Y-%m-%d' ),
75
+ homepage: row['homepage'].empty? ? nil : row['homepage'],
76
+ runtime: row['dependencies'].empty? ? [] : row['dependencies'].split('|').map {|dep| dep.strip },
77
+ }
78
+ recs << Gem.new( name: row['name'], **kwargs )
79
+ end
80
+
81
+ ## sort by
82
+ ## 1) (latest) version created and
83
+ ## 2) name
84
+ recs = recs.sort do |l,r|
85
+ res = r.version_created <=> l.version_created
86
+ res = l.name <=> r.name if res == 0
87
+ res
88
+ end
89
+
90
+ GemDataset.new( recs )
91
+ end
92
+
93
+
94
+ end # module Gems
95
+
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gemverse
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Gerald Bauer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-01-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cocos
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: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '7'
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '4.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '7'
47
+ - !ruby/object:Gem::Dependency
48
+ name: hoe
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.23'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.23'
61
+ description: gemverse gem - gem universe incl. rubygems API V1 wrapper lite; gem version
62
+ cache, gem timeline reports, 'n' more
63
+ email: opensport@googlegroups.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files:
67
+ - CHANGELOG.md
68
+ - Manifest.txt
69
+ - README.md
70
+ files:
71
+ - CHANGELOG.md
72
+ - Manifest.txt
73
+ - README.md
74
+ - Rakefile
75
+ - lib/gemverse.rb
76
+ - lib/gemverse/api.rb
77
+ - lib/gemverse/cache.rb
78
+ - lib/gemverse/gems.rb
79
+ - lib/gemverse/timeline.rb
80
+ homepage: https://github.com/rubycocos/git
81
+ licenses:
82
+ - Public Domain
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options:
86
+ - "--main"
87
+ - README.md
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 2.2.2
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubygems_version: 3.3.7
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: gemverse gem - gem universe incl. rubygems API V1 wrapper lite; gem version
105
+ cache, gem timeline reports, 'n' more
106
+ test_files: []