gemcache 0.0.1 → 0.1.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 +4 -4
- data/README.md +35 -18
- data/Rakefile +1 -14
- data/lib/gemcache/errors.rb +28 -0
- data/lib/gemcache/parsers/info_parser.rb +91 -0
- data/lib/gemcache/parsers/sem_ver_parser.rb +79 -0
- data/lib/gemcache/railtie.rb +4 -0
- data/lib/gemcache/version.rb +2 -4
- data/lib/gemcache.rb +59 -5
- data/lib/generators/gemcache/install_generator.rb +24 -0
- data/lib/tasks/gemcache_tasks.rake +4 -0
- metadata +26 -10
- data/.rubocop.yml +0 -13
- data/CHANGELOG.md +0 -0
- data/LICENSE.txt +0 -27
- data/sig/gemcache.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e64ec2ecb946ec1b360a1beab00af993f7c7fc597ae796040f8a6b892018851a
|
4
|
+
data.tar.gz: 9f509214981e8ee07970146974474a2650dc54cf237ccb25b4b6f1c49243ee86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d074637c8654d68f6722a25c7c76dae4478ec202899422f2e56ba6060522ebdb44ea1b05bc3fc71332f174803099b938ae7ee4745e7ec4afe0feb69353c0cce
|
7
|
+
data.tar.gz: ce56c725c268675c94ec0dd840af03c11b30d8ba7853b812a27ffa1bf8074f7ee986c84b5cd9d27802aa81e7eb764b9ca6f15c1f803f2b4c45c55dd214f0444c
|
data/README.md
CHANGED
@@ -1,31 +1,48 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/gemcache`. To experiment with that code, run `bin/console` for an interactive prompt.
|
1
|
+
# GemCache
|
2
|
+
GemCache is a plugin for Rails that caches Ruby gems from any [rubygems.org](https://rubygems.org) compatible host.
|
3
|
+
It is built primarly to work with [GemApi](https://github.com/pinecat/gemapi), which in turn is utilized by [Gemstruct](https://github.com/pinecat/gemstruct).
|
6
4
|
|
7
5
|
## Installation
|
6
|
+
To use GemCache in your rails project, first:
|
7
|
+
```
|
8
|
+
bundle add gemcache
|
9
|
+
```
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
then:
|
12
|
+
```
|
13
|
+
rails g gemcache:install
|
14
|
+
```
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
## Usage
|
17
|
+
```ruby
|
18
|
+
class Gemfu
|
19
|
+
include GemCache
|
16
20
|
|
17
|
-
|
21
|
+
attr_reader :name, :requirements, :info_file, :stories, :versions, :version, :info, :quick_file, :gem_file
|
18
22
|
|
19
|
-
|
23
|
+
def initialize(gem)
|
24
|
+
@name = gem.name
|
25
|
+
@requirements = gem.requirements_list
|
20
26
|
|
21
|
-
|
27
|
+
@info_file = fetch_info
|
28
|
+
@stories = InfoParser.parse(@info_file)
|
29
|
+
@versions = SemVerParser.new(@stories, @requirements, @name)
|
30
|
+
@version = @versions.available.first
|
31
|
+
@info = InfoParser.info(@version, @stories)
|
22
32
|
|
23
|
-
|
33
|
+
@quick_file = fetch_quick
|
34
|
+
@gem_file = fetch_gem
|
35
|
+
end
|
36
|
+
end
|
24
37
|
|
25
|
-
|
38
|
+
gem = Gem::Dependency.new("colorize", Gem::Requirement.new(["~> 1.0"]))
|
39
|
+
gemfu = Gemfu.new(gem)
|
26
40
|
|
27
|
-
|
41
|
+
File.binwrite("path/to/colorize.gem", gemfu.gem_file)
|
42
|
+
```
|
28
43
|
|
29
44
|
## Contributing
|
45
|
+
Bug reports and pull requests are welcome on Github at https://github.com/pinecat/gemcache/issues and https://github.com/pinecat/gemcache/pulls, respectively.
|
30
46
|
|
31
|
-
|
47
|
+
## License
|
48
|
+
The gem is available as open source under the terms of the [BSD 3-Clause License](https://opensource.org/license/bsd-3-clause/).
|
data/Rakefile
CHANGED
@@ -1,16 +1,3 @@
|
|
1
|
-
|
1
|
+
require "bundler/setup"
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
|
-
require "rake/testtask"
|
5
|
-
|
6
|
-
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs << "test"
|
8
|
-
t.libs << "lib"
|
9
|
-
t.test_files = FileList["test/**/test_*.rb"]
|
10
|
-
end
|
11
|
-
|
12
|
-
require "rubocop/rake_task"
|
13
|
-
|
14
|
-
RuboCop::RakeTask.new
|
15
|
-
|
16
|
-
task default: %i[test rubocop]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module GemCache
|
2
|
+
#
|
3
|
+
# Raised when /info/<gem> cannot be found on the remote gem host.
|
4
|
+
#
|
5
|
+
class GemNotFoundError < StandardError
|
6
|
+
def initialize(name)
|
7
|
+
super("The '#{name}' gem could not be found on '#{@@host}'")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# Raised when an unrecognized version constraint is passed in a dependency.
|
13
|
+
#
|
14
|
+
class InvalidVersionConstraintError < StandardError
|
15
|
+
def initialize(operator)
|
16
|
+
super("The semantic versioning contraint '#{operator}' is not recognized")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Raised when the version constraints are not inclusive enough to return a version.
|
22
|
+
#
|
23
|
+
class NoAvailableVersionsError < StandardError
|
24
|
+
def initialize(name)
|
25
|
+
super("There are no versions of '#{name}' that meet the specified version constraints")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module GemCache
|
2
|
+
class InfoParser
|
3
|
+
# The version of the gem from the info entry.
|
4
|
+
attr_reader :version
|
5
|
+
|
6
|
+
# A list of Gem::Dependencies from the info entry.
|
7
|
+
attr_reader :dependencies
|
8
|
+
|
9
|
+
# The SHA256 checksum of the gem from the info entry.
|
10
|
+
attr_reader :checksum
|
11
|
+
|
12
|
+
# The required Ruby version of the gem from the info entry.
|
13
|
+
attr_reader :ruby
|
14
|
+
|
15
|
+
# The required rubygems version of the gem from the info entry.
|
16
|
+
attr_reader :rubygems
|
17
|
+
|
18
|
+
def initialize(entry)
|
19
|
+
# Split line into two parts:
|
20
|
+
# (0) Semantic version and runtime dependencies
|
21
|
+
# (1) Checksum and required ruby/rubygems versions
|
22
|
+
split = entry.split("|")
|
23
|
+
ver_and_deps = split[0].strip
|
24
|
+
sha_and_ruby = split[1].strip
|
25
|
+
|
26
|
+
# Parse each part of the entry in the info file
|
27
|
+
parts = parse_version(ver_and_deps)
|
28
|
+
parse_dependencies(parts)
|
29
|
+
parts = parse_checksum(sha_and_ruby)
|
30
|
+
parts = parse_ruby(parts)
|
31
|
+
parse_rubygems(parts)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.info(version, stories)
|
35
|
+
stories.each do |s|
|
36
|
+
return s if s.version == version
|
37
|
+
end
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse(raw)
|
42
|
+
stories = []
|
43
|
+
raw.lines.drop(1).each do |l|
|
44
|
+
stories << new(l)
|
45
|
+
end
|
46
|
+
stories.reverse
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def parse_version(raw)
|
52
|
+
return if raw.blank?
|
53
|
+
|
54
|
+
parts = raw.split(" ", 2)
|
55
|
+
@version = Gem::Version.new(parts[0])
|
56
|
+
parts.drop(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_dependencies(parts)
|
60
|
+
return if parts.blank?
|
61
|
+
|
62
|
+
parts = parts.split(",").flatten
|
63
|
+
@dependencies = []
|
64
|
+
parts.each do |p|
|
65
|
+
name, requirements = p.split(":")
|
66
|
+
@dependencies << Gem::Dependency.new(name, requirements)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_checksum(raw)
|
71
|
+
return if raw.blank?
|
72
|
+
|
73
|
+
parts = raw.split(",")
|
74
|
+
@checksum = parts[0].split(":")[1]
|
75
|
+
parts.drop(1)
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_ruby(parts)
|
79
|
+
return if parts.blank?
|
80
|
+
|
81
|
+
@ruby = Gem::Dependency.new("ruby", parts[0].split(":")[1].split("&"))
|
82
|
+
parts.drop(1)
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_rubygems(parts)
|
86
|
+
return if parts.blank?
|
87
|
+
|
88
|
+
@rubygems = Gem::Dependency.new("rubygems", parts[0].split(":")[1].split("&"))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module GemCache
|
2
|
+
#
|
3
|
+
# Creates a list of appropriate gem and dependency versions
|
4
|
+
# based on semantic versioning requirements. The latest
|
5
|
+
# possible verions will always come first in the list.
|
6
|
+
#
|
7
|
+
class SemVerParser
|
8
|
+
class Constraint
|
9
|
+
def initialize(operator, version)
|
10
|
+
@operator = operator.intern
|
11
|
+
@version = Gem::Version.new(version)
|
12
|
+
end
|
13
|
+
|
14
|
+
def appropriate?(version)
|
15
|
+
version.send(@operator, @version)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# List of appropriate semantic versions, with the latest versions at the front of the list.
|
20
|
+
attr_reader :available
|
21
|
+
|
22
|
+
def initialize(stories, requirements, name)
|
23
|
+
constraints = constrain(requirements)
|
24
|
+
|
25
|
+
@available = []
|
26
|
+
stories.each do |s|
|
27
|
+
one_constraint_failed = false
|
28
|
+
constraints.each do |c|
|
29
|
+
unless c.appropriate?(s.version)
|
30
|
+
one_constraint_failed = true
|
31
|
+
break
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@available << s.version unless one_constraint_failed
|
35
|
+
end
|
36
|
+
|
37
|
+
raise NoAvailableVersionsError.new(name) if @available.blank?
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def constrain(requirements)
|
43
|
+
constraints = []
|
44
|
+
requirements.each do |r|
|
45
|
+
operator, version = r.split(" ")
|
46
|
+
|
47
|
+
case operator
|
48
|
+
when "~>"
|
49
|
+
# Pessimistic versioning (i.e. approximately greater than)
|
50
|
+
constraints << Constraint.new(">=", version)
|
51
|
+
|
52
|
+
if version.include?("-")
|
53
|
+
version = version.split("-")[0]
|
54
|
+
end
|
55
|
+
|
56
|
+
if version.include?("+")
|
57
|
+
version = version.split("+")[0]
|
58
|
+
end
|
59
|
+
|
60
|
+
mmp = version.split(".").map { |s| s.to_i }
|
61
|
+
mmp.pop unless mmp.length == 1
|
62
|
+
mmp[-1] += 1
|
63
|
+
version = mmp.join(".")
|
64
|
+
|
65
|
+
constraints << Constraint.new("<", version)
|
66
|
+
when ">", ">=", "<", "<="
|
67
|
+
constraints << Constraint.new(operator, version)
|
68
|
+
when "="
|
69
|
+
constraints << Constraint.new("==", version)
|
70
|
+
else
|
71
|
+
raise InvalidVersionConstraintError.new(operator)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
constraints
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/gemcache/version.rb
CHANGED
data/lib/gemcache.rb
CHANGED
@@ -1,8 +1,62 @@
|
|
1
|
-
|
1
|
+
require "gemcache/version"
|
2
|
+
require "gemcache/railtie"
|
3
|
+
require "gemcache/errors"
|
4
|
+
require "gemcache/parsers/info_parser"
|
5
|
+
require "gemcache/parsers/sem_ver_parser"
|
6
|
+
require "net/http"
|
7
|
+
require "stringio"
|
8
|
+
require "rubygems/package"
|
9
|
+
require "rubygems/indexer"
|
2
10
|
|
3
|
-
|
11
|
+
module GemCache
|
12
|
+
#
|
13
|
+
# The host of the remote gem server.
|
14
|
+
# The default is https://rubygems.org.
|
15
|
+
#
|
16
|
+
mattr_accessor :host
|
17
|
+
@@host = "https://rubygems.org"
|
4
18
|
|
5
|
-
|
6
|
-
|
7
|
-
#
|
19
|
+
#
|
20
|
+
# Set configuration options.
|
21
|
+
# This is typically done in the initializer ('rails g gemcache:install')
|
22
|
+
#
|
23
|
+
def self.setup
|
24
|
+
yield self
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Fetch required gem info file.
|
29
|
+
#
|
30
|
+
# @return [String] The (YAML based?) info file.
|
31
|
+
#
|
32
|
+
def fetch_info
|
33
|
+
info_uri = URI.parse("#{@@host}/info/#{@name}")
|
34
|
+
response = Net::HTTP.get_response(info_uri)
|
35
|
+
raise GemNotFoundError.new(@name) unless response.is_a? Net::HTTPSuccess
|
36
|
+
response.body
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Fetch required gem quick file.
|
41
|
+
#
|
42
|
+
# @return [String] The binary (compressed) quick file.
|
43
|
+
#
|
44
|
+
def fetch_quick
|
45
|
+
quick_uri = URI.parse("#{@@host}/quick/Marshal.4.8/#{@name}-#{@version}.gemspec.rz")
|
46
|
+
response = Net::HTTP.get_response(quick_uri)
|
47
|
+
raise GemNotFoundError.new(@name) unless response.is_a? Net::HTTPSuccess
|
48
|
+
response.body
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Fetch required gem file.
|
53
|
+
#
|
54
|
+
# @return [String] The binary (compressed) gem file.
|
55
|
+
#
|
56
|
+
def fetch_gem
|
57
|
+
gem_uri = URI.parse("#{@@host}/gems/#{@name}-#{@version}.gem")
|
58
|
+
response = Net::HTTP.get_response(gem_uri)
|
59
|
+
raise GemNotFoundError(@name) unless response.is_a? Net::HTTPSuccess
|
60
|
+
response.body
|
61
|
+
end
|
8
62
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/base"
|
4
|
+
|
5
|
+
module GemCache
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
desc "Create an initializer file for Gemcache configuration options"
|
8
|
+
def create_initializer_file
|
9
|
+
create_file "config/initializers/gemcache.rb", <<~RUBY
|
10
|
+
# frozen_string_literal: true
|
11
|
+
|
12
|
+
# GemCache configuration options.
|
13
|
+
GemCache.setup do |config|
|
14
|
+
# The remote host to cache ruby gems from. This option may be overriden
|
15
|
+
# directly or by setting the 'RAILS_GEMCACHE_HOST' environment
|
16
|
+
# variable. The default option is https://rubygems.org. The remote host
|
17
|
+
# must follow the distrubution pattern of rubygems.org, otherwise
|
18
|
+
# GemCache will be unable to fetch gems from it.
|
19
|
+
config.host = ENV["RAILS_GEMCACHE_HOST"] || "https://rubygems.org"
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,30 +1,46 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gemcache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rory Dudley
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2023-08-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.0.7.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 7.0.7.2
|
27
|
+
description: Cache Ruby gems from a rubygems.org compatible host.
|
14
28
|
email:
|
15
29
|
- rory.dudley@gmail.com
|
16
30
|
executables: []
|
17
31
|
extensions: []
|
18
32
|
extra_rdoc_files: []
|
19
33
|
files:
|
20
|
-
- ".rubocop.yml"
|
21
|
-
- CHANGELOG.md
|
22
|
-
- LICENSE.txt
|
23
34
|
- README.md
|
24
35
|
- Rakefile
|
25
36
|
- lib/gemcache.rb
|
37
|
+
- lib/gemcache/errors.rb
|
38
|
+
- lib/gemcache/parsers/info_parser.rb
|
39
|
+
- lib/gemcache/parsers/sem_ver_parser.rb
|
40
|
+
- lib/gemcache/railtie.rb
|
26
41
|
- lib/gemcache/version.rb
|
27
|
-
-
|
42
|
+
- lib/generators/gemcache/install_generator.rb
|
43
|
+
- lib/tasks/gemcache_tasks.rake
|
28
44
|
homepage: https://github.com/pinecat/gemcache
|
29
45
|
licenses:
|
30
46
|
- BSD 3-Clause
|
@@ -41,7 +57,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
57
|
requirements:
|
42
58
|
- - ">="
|
43
59
|
- !ruby/object:Gem::Version
|
44
|
-
version:
|
60
|
+
version: '0'
|
45
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
62
|
requirements:
|
47
63
|
- - ">="
|
data/.rubocop.yml
DELETED
data/CHANGELOG.md
DELETED
File without changes
|
data/LICENSE.txt
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
Copyright (c) 2023, Rory Dudley
|
2
|
-
All rights reserved.
|
3
|
-
|
4
|
-
Redistribution and use in source and binary forms, with or without
|
5
|
-
modification, are permitted provided that the following conditions are met:
|
6
|
-
|
7
|
-
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
-
list of conditions and the following disclaimer.
|
9
|
-
|
10
|
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
-
this list of conditions and the following disclaimer in the documentation
|
12
|
-
and/or other materials provided with the distribution.
|
13
|
-
|
14
|
-
3. Neither the name of the copyright holder nor the names of its
|
15
|
-
contributors may be used to endorse or promote products derived from
|
16
|
-
this software without specific prior written permission.
|
17
|
-
|
18
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/sig/gemcache.rbs
DELETED