gemcache 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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