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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c76e76eeb8a93262347ce8fea27326b005c739d7bbc5e5e438ce5fee055b1e2
4
- data.tar.gz: 23ecd2076a905c840a542f608c6d62ebb0ca2fd92f856236752450f6a7487b4e
3
+ metadata.gz: e64ec2ecb946ec1b360a1beab00af993f7c7fc597ae796040f8a6b892018851a
4
+ data.tar.gz: 9f509214981e8ee07970146974474a2650dc54cf237ccb25b4b6f1c49243ee86
5
5
  SHA512:
6
- metadata.gz: a3491b0461e394d32e250448f0013cefe756c1d880abaec7892ab52065324ae55fc929965a0a8421e5ce85a8e00c57ecf43eaa7fa078d47a54eb89afd2359509
7
- data.tar.gz: 532c782579338c57218880057283d2dcdc98ba069fe50dd22984a59fc81cb1a6c0fb7cc634a099dc57feb0b3156a953ff22e5607b58bd995da9d954310141a01
6
+ metadata.gz: 7d074637c8654d68f6722a25c7c76dae4478ec202899422f2e56ba6060522ebdb44ea1b05bc3fc71332f174803099b938ae7ee4745e7ec4afe0feb69353c0cce
7
+ data.tar.gz: ce56c725c268675c94ec0dd840af03c11b30d8ba7853b812a27ffa1bf8074f7ee986c84b5cd9d27802aa81e7eb764b9ca6f15c1f803f2b4c45c55dd214f0444c
data/README.md CHANGED
@@ -1,31 +1,48 @@
1
- # Gemcache
2
-
3
- TODO: Delete this and the text below, and describe your gem
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
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
-
11
- Install the gem and add to the application's Gemfile by executing:
11
+ then:
12
+ ```
13
+ rails g gemcache:install
14
+ ```
12
15
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
14
-
15
- If bundler is not being used to manage dependencies, install the gem by executing:
16
+ ## Usage
17
+ ```ruby
18
+ class Gemfu
19
+ include GemCache
16
20
 
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
21
+ attr_reader :name, :requirements, :info_file, :stories, :versions, :version, :info, :quick_file, :gem_file
18
22
 
19
- ## Usage
23
+ def initialize(gem)
24
+ @name = gem.name
25
+ @requirements = gem.requirements_list
20
26
 
21
- TODO: Write usage instructions here
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
- ## Development
33
+ @quick_file = fetch_quick
34
+ @gem_file = fetch_gem
35
+ end
36
+ end
24
37
 
25
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
38
+ gem = Gem::Dependency.new("colorize", Gem::Requirement.new(["~> 1.0"]))
39
+ gemfu = Gemfu.new(gem)
26
40
 
27
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
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
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/gemcache.
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
- # frozen_string_literal: true
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
@@ -0,0 +1,4 @@
1
+ module GemCache
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
- module Gemcache
4
- VERSION = "0.0.1"
1
+ module GemCache
2
+ VERSION = "0.1.0"
5
3
  end
data/lib/gemcache.rb CHANGED
@@ -1,8 +1,62 @@
1
- # frozen_string_literal: true
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
- require_relative "gemcache/version"
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
- module Gemcache
6
- class Error < StandardError; end
7
- # Your code goes here...
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
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :gemcache do
3
+ # # Task goes here
4
+ # 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.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rory Dudley
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-23 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Cache Ruby gems from an https://rubygems.org compatible host.
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
- - sig/gemcache.rbs
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: 2.6.0
60
+ version: '0'
45
61
  required_rubygems_version: !ruby/object:Gem::Requirement
46
62
  requirements:
47
63
  - - ">="
data/.rubocop.yml DELETED
@@ -1,13 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 2.6
3
-
4
- Style/StringLiterals:
5
- Enabled: true
6
- EnforcedStyle: double_quotes
7
-
8
- Style/StringLiteralsInInterpolation:
9
- Enabled: true
10
- EnforcedStyle: double_quotes
11
-
12
- Layout/LineLength:
13
- Max: 120
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
@@ -1,4 +0,0 @@
1
- module Gemcache
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end