fustigit 0.1.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 +7 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE +13 -0
- data/README.md +86 -0
- data/Rakefile +30 -0
- data/lib/fustigit.rb +5 -0
- data/lib/fustigit/version.rb +5 -0
- data/lib/uri/git.rb +17 -0
- data/lib/uri/rsync.rb +8 -0
- data/lib/uri/scp.rb +16 -0
- data/lib/uri/ssh.rb +16 -0
- data/lib/uri/triplets.rb +123 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/uri/fustigit_spec.rb +31 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 551f4ad9ed874b976c1de00a5289a1352d946d64
|
4
|
+
data.tar.gz: 1954e7f60f0c10a423f8a2cc0f16d0a09ba4d085
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fe5bbee3df3198f8d9d93f7cc88a27c1609c2ee952e25a65884016f07b5e44b15a3d98cbe0c88e773b9f8806dc6788172708eb59be85d8bacee65b353ed88d61
|
7
|
+
data.tar.gz: 50f90dbc0e42dca704a759d2ab68feb3a2ab326a380b4733ff848099b45a2e1fbd4e54850c587eb72befbabe34a633b37b908ff41cfdb7ac3d08675767896861
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
## Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
This project attempts to adhere to [Semantic Versioning](http://semver.org/).
|
4
|
+
This changelog attempts to adhere to [Keep a CHANGELOG](http://keepachangelog.com/).
|
5
|
+
|
6
|
+
## [0.1.1] - 09 May 2016
|
7
|
+
### Added
|
8
|
+
- Fix Ruby 2.2.0 and 2.3.0 support
|
9
|
+
- Add JRuby to Travis checks
|
10
|
+
- Make Rubocop print cop names by default
|
11
|
+
|
12
|
+
## [0.1.0] - 09 May 2016
|
13
|
+
### Added
|
14
|
+
- Initial release
|
15
|
+
- README covers usage, licensing, and rationale
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2014-2016 Ryan McKern
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
## Fustigit: gorram Git addresses
|
2
|
+
|
3
|
+
<dl>
|
4
|
+
<dt>Fustigate — (verb) fus·ti·gate </dd>
|
5
|
+
<dd>To beat with a club; cudgel.</dd>
|
6
|
+
<dd>To criticize harshly.</dd>
|
7
|
+
</dl>
|
8
|
+
|
9
|
+
[](https://travis-ci.org/mckern/fustigit)
|
10
|
+
|
11
|
+
### TL;DR
|
12
|
+
|
13
|
+
Fustigit will let you "parse" SCP-like address triplets using Ruby's baked-in [URI library](http://ruby-doc.org/stdlib-2.3.1/libdoc/uri/rdoc/index.html) (... and just a *moderate* amount of monkey-patching) and turn them into probably-valid URI objects.
|
14
|
+
|
15
|
+
### What's a Triplet?
|
16
|
+
|
17
|
+
<a href="https://www.debuggex.com/r/UF-0ESZoWXFewi8q"><img src="https://www.debuggex.com/i/UF-0ESZoWXFewi8q.png"></a>
|
18
|
+
|
19
|
+
A triplet is a format for specifying a remote resource, much like a URI. It looks like this:
|
20
|
+
|
21
|
+
# The username is optional but the hostname and pathname are not
|
22
|
+
<username>@<hostname>:<pathname>
|
23
|
+
|
24
|
+
Triplets predate the [original ratification of the URI RFC](https://tools.ietf.org/html/rfc2396), and are *tricksy* to parse if you're playing by URI rules since they don't define a protocol and they use a colon to separate the hostname from the pathname. `scp` and `git` are the two most common tools that still use triplets.
|
25
|
+
|
26
|
+
### Why would I need to parse triplets?
|
27
|
+
|
28
|
+
The answer is usually "[Git](https://git-scm.com)" (but sometimes it's `scp`). Git supports a conveniently inconvenient number of formats for expressing where a remote repository is located and/or what protocol should be used to connect to it. Some of them are perfectly valid URIs. Some of them are not. It's the ones that are not that may be a problem.
|
29
|
+
|
30
|
+
```yaml
|
31
|
+
---
|
32
|
+
# These won't parse and they're both SUPER common
|
33
|
+
- example.com:path/to/repo.git
|
34
|
+
- git@example.com:user/project.git
|
35
|
+
|
36
|
+
# But these will parse, which is great since they're also SUPER common
|
37
|
+
- https://example.com/user/project.git
|
38
|
+
- http://example.com/user/project.git
|
39
|
+
```
|
40
|
+
|
41
|
+
Enter Fustigit.
|
42
|
+
|
43
|
+
### How (do I use this)?
|
44
|
+
|
45
|
+
<s>Carelessly</s> Without a care in the world!
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
>> URI.parse "git@github.com:mckern/fustigit.git"
|
49
|
+
URI::InvalidURIError: bad URI(is not URI?): git@github.com:mckern/fustigit.git [/some/path/for/ruby/lib/ruby/2.1.0/uri/common.rb:176:in `split']
|
50
|
+
>> require 'fustigit'
|
51
|
+
=> true
|
52
|
+
>> uri = URI.parse "git@github.com:mckern/fustigit.git"
|
53
|
+
=> #<URI::SSH:0x007f8459131f98 URL:git@github.com:mckern/fustigit.git>
|
54
|
+
>> uri.host
|
55
|
+
=> "github.com"
|
56
|
+
>> uri.user
|
57
|
+
=> "git"
|
58
|
+
>> uri.path
|
59
|
+
=> "mckern/fustigit.git"
|
60
|
+
>> uri.to_s
|
61
|
+
=> "git@github.com:mckern/fustigit.git"
|
62
|
+
>>
|
63
|
+
```
|
64
|
+
|
65
|
+
### How (does it work)?
|
66
|
+
|
67
|
+
Careful use of `Module#prepend` and `Module#extend` in `URI` and `URI::Parser`, judicious use of regular expressions, and by defining a few new `URI` subclasses: `URI::Git`, `URI::SSH`, `URI::SCP`, and `URI::RSYNC`. Some of these classes then have the `Triplet` module mixed in, which helps smooth over the conversion between a valid RFC-compliant URI and an address triplet.
|
68
|
+
|
69
|
+
### What if I'm using `Addressable::URI` instead of `::URI`?
|
70
|
+
|
71
|
+
Take a look at Martin Emde's [Gitable](https://github.com/martinemde/gitable), which extends `Addressable::URI` with additional support for Git addresses.
|
72
|
+
|
73
|
+
### Support & contribution?
|
74
|
+
|
75
|
+
In the spirit of Jordan Sissel (a hero to admins and operations people everywhere), if fustigit is not helping you parse weird Git addresses, then there is a bug in fustigit. Please open an issue or submit a pull request if something doesn't work.
|
76
|
+
|
77
|
+
### License
|
78
|
+
|
79
|
+
Fustigate is licensed under the Apache License, Version 2.0.
|
80
|
+
|
81
|
+
> "When in doubt, use brute force."
|
82
|
+
> ― <cite>Ken Thompson</cite>
|
83
|
+
|
84
|
+
### Maintainer
|
85
|
+
|
86
|
+
Ryan McKern <ryan@orangefort.com>
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rake/testtask"
|
4
|
+
require "rubocop/rake_task"
|
5
|
+
|
6
|
+
desc "Test fustigit"
|
7
|
+
namespace :test do
|
8
|
+
Rake::TestTask.new(:spec) do |test|
|
9
|
+
test.libs << "spec"
|
10
|
+
test.pattern = "spec/**/*_spec.rb"
|
11
|
+
test.verbose = false
|
12
|
+
test.warning = false
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Test fustigit and calculate test coverage"
|
16
|
+
task :coverage do
|
17
|
+
ENV["COVERAGE"] = "true"
|
18
|
+
Rake::Task["test:spec"].invoke
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run RuboCop"
|
23
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
24
|
+
task.patterns = ["lib/**/*.rb"]
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Run all spec tests and linters"
|
28
|
+
task check: %w(test:spec rubocop)
|
29
|
+
|
30
|
+
task default: :check
|
data/lib/fustigit.rb
ADDED
data/lib/uri/git.rb
ADDED
data/lib/uri/rsync.rb
ADDED
data/lib/uri/scp.rb
ADDED
data/lib/uri/ssh.rb
ADDED
data/lib/uri/triplets.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
# Triplets is a mix-in for subclasses of URI::Generic, which
|
4
|
+
# allows URI to use SCP-style Triplet URLs in a somewhat more
|
5
|
+
# meaningful way.
|
6
|
+
module Triplets
|
7
|
+
attr_accessor :scheme, :user, :host, :path
|
8
|
+
|
9
|
+
# @return [String] a string representation of the URI components
|
10
|
+
# as an SCP-style Triplet
|
11
|
+
def triplet
|
12
|
+
str = ""
|
13
|
+
str << "#{user}@" if user && !user.empty?
|
14
|
+
str << "#{host}:#{path}".squeeze("/")
|
15
|
+
end
|
16
|
+
private :triplet
|
17
|
+
|
18
|
+
# @return [String] a string representation of the URI components
|
19
|
+
# as a valid RFC compliant URI
|
20
|
+
# rubocop:disable Metrics/AbcSize
|
21
|
+
def rfc_uri
|
22
|
+
str = ""
|
23
|
+
str << "#{scheme}://" if scheme
|
24
|
+
str << "#{user}@" if user
|
25
|
+
if port && port != self.class::DEFAULT_PORT
|
26
|
+
host_info [host, port].join(":")
|
27
|
+
str << [host_info, path].join("/").squeeze("/")
|
28
|
+
else
|
29
|
+
str << [host, path].join("/").squeeze("/")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
private :rfc_uri
|
33
|
+
|
34
|
+
# if self.path is a relative path, assume that this was parsed
|
35
|
+
# as a triplet and return a Triplet. Otherwise, assume that
|
36
|
+
# this is a valid URI and print an RFC compliant URI. This
|
37
|
+
# may not be the most robust method of determining if a
|
38
|
+
# triplet should be used, but everything starts someplace.
|
39
|
+
def to_s
|
40
|
+
return triplet if relative?
|
41
|
+
rfc_uri
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# A SCP Triplet is *not* a canonical URI. It doesn't follow any RFCs that
|
46
|
+
# I've been able to find, and it's difficult to reason about if you
|
47
|
+
# try to force it into a URI::Generic as-is. TripletInterruptus provides
|
48
|
+
# helper methods that preserve the upstream behavior of URI::Generic
|
49
|
+
# but extend it just enough that it doesn't choke on SCP Triplets if
|
50
|
+
# they're passed.
|
51
|
+
module TripletInterruptus
|
52
|
+
# Determine if a string can be teased apart into URI-like components
|
53
|
+
TRIPLET = %r{\A(?:(?<userinfo>.+)[@]+)?(?<host>[\w.]+):(?<path>.*)\z}
|
54
|
+
|
55
|
+
# Determine if a string is prefixed with a URI scheme like http:// or ssh://
|
56
|
+
SCHEME = %r{\A(?:(?<scheme>[a-z]+)://)}
|
57
|
+
|
58
|
+
def parse(uri)
|
59
|
+
return build_triplet(uri) if triplet?(uri)
|
60
|
+
super(uri)
|
61
|
+
end
|
62
|
+
|
63
|
+
def triplet?(address)
|
64
|
+
address.match(TRIPLET) && !address.match(SCHEME)
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_triplet(address)
|
68
|
+
values = parse_triplet(address)
|
69
|
+
return nil unless values
|
70
|
+
URI.scheme_list[URI.default_triplet_type].build(values)
|
71
|
+
end
|
72
|
+
private :build_triplet
|
73
|
+
|
74
|
+
def parse_triplet(address)
|
75
|
+
parts = address.match(TRIPLET)
|
76
|
+
return nil unless parts
|
77
|
+
Hash[parts.names.map(&:to_sym).zip(parts.captures)]
|
78
|
+
end
|
79
|
+
private :parse_triplet
|
80
|
+
end
|
81
|
+
|
82
|
+
module TripletHandling
|
83
|
+
TRIPLET_CLASSES = %w(Git SCP SSH).freeze
|
84
|
+
|
85
|
+
def self.included(base)
|
86
|
+
base.extend(TripletHandling)
|
87
|
+
end
|
88
|
+
|
89
|
+
def default_triplet_type
|
90
|
+
@default_triplet_type ||= "SSH"
|
91
|
+
end
|
92
|
+
|
93
|
+
def default_triplet_type=(value)
|
94
|
+
unless TRIPLET_CLASSES.include?(value)
|
95
|
+
raise ArgumentError, "'#{value}' is not one of: #{TRIPLET_CLASSES.join(', ')}"
|
96
|
+
end
|
97
|
+
@default_triplet_type = value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Reopen URI and include TripletHandling (which will then
|
102
|
+
# extend URI to add triplet-specific class methods).
|
103
|
+
module URI
|
104
|
+
include TripletHandling
|
105
|
+
end
|
106
|
+
|
107
|
+
module URI
|
108
|
+
# Reopen URI::Parser and prepend TripletInterruptus. This
|
109
|
+
# allows us to hook into URI::Parser.parse and attempt to
|
110
|
+
# parse a triplet before URI::Parser can reject it. Otherwise
|
111
|
+
# fall through to the original URI::Parser.parse method.
|
112
|
+
#
|
113
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.0")
|
114
|
+
# rubocop:disable Style/ClassAndModuleCamelCase
|
115
|
+
class RFC3986_Parser
|
116
|
+
prepend TripletInterruptus
|
117
|
+
end
|
118
|
+
else
|
119
|
+
class Parser
|
120
|
+
prepend TripletInterruptus
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "minitest/spec"
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "minitest/reporters"
|
5
|
+
require "simplecov"
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
Minitest::Reporters.use! Minitest::Reporters::DefaultReporter.new
|
9
|
+
|
10
|
+
if ENV["COVERAGE"]
|
11
|
+
SimpleCov.start do
|
12
|
+
# exclude common Bundler locations
|
13
|
+
%w(.bundle vendor).each { |dir| add_filter dir }
|
14
|
+
# exclude test code
|
15
|
+
add_filter "spec"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def fixture(path)
|
20
|
+
path = File.join(File.dirname(__FILE__), "fixtures", path)
|
21
|
+
File.read(path)
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fustigit"
|
3
|
+
|
4
|
+
describe URI do
|
5
|
+
@git_repos = JSON.load(fixture("formats.json"))
|
6
|
+
@git_repos["URIs"].each do |protocol, repos|
|
7
|
+
repos.each do |repo|
|
8
|
+
describe %(#parse takes given URI "#{repo}") do
|
9
|
+
it "returns URI::#{protocol}" do
|
10
|
+
URI.parse(repo).is_a?(URI.const_get(protocol)).must_equal true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
@git_repos["paths"].each do |repo|
|
17
|
+
describe %(#parse takes path "#{repo}") do
|
18
|
+
it "returns URI::Generic" do
|
19
|
+
URI.parse(repo).is_a?(URI::Generic).must_equal true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@git_repos["triplets"].each do |repo|
|
25
|
+
describe %(#parse takes triplet "#{repo}") do
|
26
|
+
it "returns URI::#{URI.default_triplet_type}" do
|
27
|
+
URI.parse(repo).is_a?(URI.const_get(URI.default_triplet_type)).must_equal true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fustigit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan McKern
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: '"Parse" SCP-like address triplets with the Standard Ruby URI Library.'
|
14
|
+
email: ryan@orangefort.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- CHANGELOG.md
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- lib/fustigit.rb
|
24
|
+
- lib/fustigit/version.rb
|
25
|
+
- lib/uri/git.rb
|
26
|
+
- lib/uri/rsync.rb
|
27
|
+
- lib/uri/scp.rb
|
28
|
+
- lib/uri/ssh.rb
|
29
|
+
- lib/uri/triplets.rb
|
30
|
+
- spec/spec_helper.rb
|
31
|
+
- spec/uri/fustigit_spec.rb
|
32
|
+
homepage: http://github.com/mckern/fustigit
|
33
|
+
licenses:
|
34
|
+
- Apache-2.0
|
35
|
+
metadata: {}
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 2.1.0
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
requirements: []
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 2.6.4
|
53
|
+
signing_key:
|
54
|
+
specification_version: 3
|
55
|
+
summary: Use URI to "parse" SCP-like triplets
|
56
|
+
test_files:
|
57
|
+
- Rakefile
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
- spec/uri/fustigit_spec.rb
|
60
|
+
has_rdoc:
|