Skip to content

Commit eafca30

Browse files
committed
Extract logic from Dependency, update supported syntax
1 parent aae1479 commit eafca30

File tree

4 files changed

+176
-151
lines changed

4 files changed

+176
-151
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
require "./spec_helper"
2+
require "../../src/dependency_definition"
3+
4+
private def expect_parses(value, resolver_key : String, source : String, requirement : Shards::Requirement)
5+
Shards::DependencyDefinition.parts_from_cli(value).should eq(Shards::DependencyDefinition::Parts.new(resolver_key: resolver_key, source: source, requirement: requirement))
6+
end
7+
8+
module Shards
9+
describe DependencyDefinition do
10+
it ".parts_from_cli" do
11+
# GitHub short syntax
12+
expect_parses("github:foo/bar", "github", "foo/bar", Any)
13+
expect_parses("github:Foo/[email protected]", "github", "Foo/Bar", VersionReq.new("~> 1.2.3"))
14+
15+
# GitHub urls
16+
expect_parses("https://github.com/foo/bar", "github", "foo/bar", Any)
17+
18+
# GitHub urls from clone popup
19+
expect_parses("https://github.com/foo/bar.git", "github", "foo/bar", Any)
20+
expect_parses("[email protected]:foo/bar.git", "git", "[email protected]:foo/bar.git", Any)
21+
22+
# GitLab short syntax
23+
expect_parses("gitlab:foo/bar", "gitlab", "foo/bar", Any)
24+
25+
# GitLab urls
26+
expect_parses("https://gitlab.com/foo/bar", "gitlab", "foo/bar", Any)
27+
28+
# GitLab urls from clone popup
29+
expect_parses("https://gitlab.com/foo/bar.git", "gitlab", "foo/bar", Any)
30+
expect_parses("[email protected]:foo/bar.git", "git", "[email protected]:foo/bar.git", requirement: Any)
31+
32+
# Bitbucket short syntax
33+
expect_parses("bitbucket:foo/bar", "bitbucket", "foo/bar", Any)
34+
35+
# bitbucket urls
36+
expect_parses("https://bitbucket.com/foo/bar", "bitbucket", "foo/bar", Any)
37+
38+
# Git convenient syntax since resolver matches scheme
39+
expect_parses("git://git.example.org/crystal-library.git", "git", "git://git.example.org/crystal-library.git", Any)
40+
expect_parses("[email protected]:foo/bar.git", "git", "[email protected]:foo/bar.git", Any)
41+
42+
# Local paths
43+
local_absolute = "/an/absolute/path"
44+
local_relative = "an/relative/path"
45+
46+
# Path short syntax
47+
expect_parses("./#{local_relative}", "path", "./#{local_relative}", Any)
48+
expect_parses("../#{local_relative}", "path", "../#{local_relative}", Any)
49+
expect_parses(".\\relative\\windows", "path", ".\\relative\\windows", Any)
50+
expect_parses("..\\relative\\windows", "path", "..\\relative\\windows", Any)
51+
# Path file schema
52+
expect_parses("file://#{local_relative}", "path", local_relative, Any)
53+
expect_parses("file://#{local_absolute}", "path", local_absolute, Any)
54+
# Path resolver syntax
55+
expect_parses("path:#{local_absolute}", "path", local_absolute, Any)
56+
expect_parses("path:#{local_relative}", "path", local_relative, Any)
57+
# Other resolvers short
58+
expect_parses("git:git://git.example.org/crystal-library.git", "git", "git://git.example.org/crystal-library.git", Any)
59+
end
60+
end
61+
end

spec/unit/dependency_spec.cr

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -71,49 +71,6 @@ module Shards
7171
parse_dependency({foo: {git: "", tag: "rc-1.0"}}).to_s.should eq("foo (tag rc-1.0)")
7272
parse_dependency({foo: {git: "", commit: "4478d8afe8c728f44b47d3582a270423cd7fc07d"}}).to_s.should eq("foo (commit 4478d8a)")
7373
end
74-
75-
it ".parts_from_cli" do
76-
# GitHub short syntax
77-
Dependency.parts_from_cli("github:foo/bar").should eq({resolver_key: "github", source: "foo/bar", requirement: Any})
78-
Dependency.parts_from_cli("github:Foo/[email protected]").should eq({resolver_key: "github", source: "Foo/Bar", requirement: VersionReq.new("~> 1.2.3")})
79-
80-
# GitHub urls
81-
Dependency.parts_from_cli("https://github.com/foo/bar").should eq({resolver_key: "github", source: "foo/bar", requirement: Any})
82-
Dependency.parts_from_cli("https://github.com/Foo/Bar/commit/000000").should eq({resolver_key: "github", source: "Foo/Bar", requirement: GitCommitRef.new("000000")})
83-
Dependency.parts_from_cli("https://github.com/Foo/Bar/tree/v1.2.3").should eq({resolver_key: "github", source: "Foo/Bar", requirement: GitTagRef.new("v1.2.3")})
84-
Dependency.parts_from_cli("https://github.com/Foo/Bar/tree/some/branch").should eq({resolver_key: "github", source: "Foo/Bar", requirement: GitBranchRef.new("some/branch")})
85-
86-
# GitLab short syntax
87-
Dependency.parts_from_cli("gitlab:foo/bar").should eq({resolver_key: "gitlab", source: "foo/bar", requirement: Any})
88-
89-
# GitLab urls
90-
Dependency.parts_from_cli("https://gitlab.com/foo/bar").should eq({resolver_key: "gitlab", source: "foo/bar", requirement: Any})
91-
92-
# Bitbucket short syntax
93-
Dependency.parts_from_cli("bitbucket:foo/bar").should eq({resolver_key: "bitbucket", source: "foo/bar", requirement: Any})
94-
95-
# bitbucket urls
96-
Dependency.parts_from_cli("https://bitbucket.com/foo/bar").should eq({resolver_key: "bitbucket", source: "foo/bar", requirement: Any})
97-
98-
# Git convenient syntax since resolver matches scheme
99-
Dependency.parts_from_cli("git://git.example.org/crystal-library.git").should eq({resolver_key: "git", source: "git://git.example.org/crystal-library.git", requirement: Any})
100-
101-
# Local paths
102-
local_absolute = File.join(tmp_path, "local")
103-
local_relative = File.join("spec", ".repositories", "local") # rel_path is relative to integration spec
104-
Dir.mkdir_p(local_absolute)
105-
106-
# Path short syntax
107-
Dependency.parts_from_cli(local_absolute).should eq({resolver_key: "path", source: local_absolute, requirement: Any})
108-
Dependency.parts_from_cli(local_relative).should eq({resolver_key: "path", source: local_relative, requirement: Any})
109-
110-
# Path resolver syntax
111-
Dependency.parts_from_cli("path:#{local_absolute}").should eq({resolver_key: "path", source: local_absolute, requirement: Any})
112-
Dependency.parts_from_cli("path:#{local_relative}").should eq({resolver_key: "path", source: local_relative, requirement: Any})
113-
114-
# Other resolvers short
115-
Dependency.parts_from_cli("git:git://git.example.org/crystal-library.git").should eq({resolver_key: "git", source: "git://git.example.org/crystal-library.git", requirement: Any})
116-
end
11774
end
11875
end
11976

src/dependency.cr

Lines changed: 1 addition & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -7,105 +7,8 @@ module Shards
77
property name : String
88
property resolver : Resolver
99
property requirement : Requirement
10-
# resolver's key and source are normalized. We preserve the key and source to be used
11-
# in the shard.yml file in these field. This is used to generate the shard.yml file
12-
# in a more human-readable way.
13-
# A Dependency can still be created without them, but it will not be possible to
14-
# generate the shard.yml file.
15-
property! resolver_key : String
16-
property! source : String
17-
18-
def initialize(@name : String, @resolver : Resolver, @requirement : Requirement = Any, @resolver_key : String? = nil, @source : String? = nil)
19-
end
20-
21-
# Parse a dependency from a CLI argument
22-
def self.from_cli(value : String) : Dependency
23-
parts = parts_from_cli(value)
2410

25-
# We need to check the actual shard name to create a dependency.
26-
# This requires getting the actual spec file from some matching version.
27-
resolver = Resolver.find_resolver(parts[:resolver_key], "unknown", parts[:source])
28-
version = resolver.versions_for(parts[:requirement]).first || raise Shards::Error.new("No versions found for dependency: #{value}")
29-
spec = resolver.spec(version)
30-
name = spec.name || raise Shards::Error.new("No name found for dependency: #{value}")
31-
32-
Dependency.new(name, resolver, parts[:requirement], parts[:resolver_key], parts[:source])
33-
end
34-
35-
# :nodoc:
36-
#
37-
# Parse the dependency from a CLI argument
38-
# and return the parts needed to create the proper dependency.
39-
#
40-
# Split to allow better unit testing.
41-
def self.parts_from_cli(value : String) : {resolver_key: String, source: String, requirement: Requirement}
42-
resolver_key = nil
43-
source = ""
44-
requirement = Any
45-
46-
if File.directory?(value)
47-
resolver_key = "path"
48-
source = value
49-
end
50-
51-
if value.starts_with?("https://github.com")
52-
resolver_key = "github"
53-
uri = URI.parse(value)
54-
source = uri.path[1..-1] # drop first "/""
55-
56-
components = source.split("/")
57-
case components[2]?
58-
when "commit"
59-
source = "#{components[0]}/#{components[1]}"
60-
requirement = GitCommitRef.new(components[3])
61-
when "tree"
62-
source = "#{components[0]}/#{components[1]}"
63-
requirement = if components[3].starts_with?("v")
64-
GitTagRef.new(components[3])
65-
else
66-
GitBranchRef.new(components[3..-1].join("/"))
67-
end
68-
end
69-
end
70-
71-
if value.starts_with?("https://gitlab.com")
72-
resolver_key = "gitlab"
73-
uri = URI.parse(value)
74-
source = uri.path[1..-1] # drop first "/""
75-
end
76-
77-
if value.starts_with?("https://bitbucket.com")
78-
resolver_key = "bitbucket"
79-
uri = URI.parse(value)
80-
source = uri.path[1..-1] # drop first "/""
81-
end
82-
83-
if value.starts_with?("git://")
84-
resolver_key = "git"
85-
source = value
86-
end
87-
88-
unless resolver_key
89-
Resolver.resolver_keys.each do |key|
90-
key_schema = "#{key}:"
91-
if value.starts_with?(key_schema)
92-
resolver_key = key
93-
source = value.sub(key_schema, "")
94-
95-
# narrow down requirement
96-
if source.includes?("@")
97-
source, version = source.split("@")
98-
requirement = VersionReq.new("~> #{version}")
99-
end
100-
101-
break
102-
end
103-
end
104-
end
105-
106-
raise Shards::Error.new("Invalid dependency format: #{value}") unless resolver_key
107-
108-
{resolver_key: resolver_key, source: source, requirement: requirement}
11+
def initialize(@name : String, @resolver : Resolver, @requirement : Requirement = Any)
10912
end
11013

11114
def self.from_yaml(pull : YAML::PullParser)
@@ -151,16 +54,6 @@ module Shards
15154
end
15255
end
15356

154-
# Used to generate the shard.yml file.
155-
def to_shard_yaml(yaml : YAML::Builder)
156-
yaml.scalar name
157-
yaml.mapping do
158-
yaml.scalar resolver_key
159-
yaml.scalar source
160-
requirement.to_yaml(yaml)
161-
end
162-
end
163-
16457
def as_package?
16558
version =
16659
case req = @requirement

src/dependency_definition.cr

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
require "./dependency"
2+
3+
module Shards
4+
class DependencyDefinition
5+
record Parts, resolver_key : String, source : String, requirement : Requirement
6+
7+
property dependency : Dependency
8+
# resolver's key and source are normalized. We preserve the key and source to be used
9+
# in the shard.yml file in these field. This is used to generate the shard.yml file
10+
# in a more human-readable way.
11+
property resolver_key : String
12+
property source : String
13+
14+
def initialize(@dependency : Dependency, @resolver_key : String, @source : String)
15+
end
16+
17+
# Used to generate the shard.yml file.
18+
def to_yaml(yaml : YAML::Builder)
19+
yaml.scalar dependency.name
20+
yaml.mapping do
21+
yaml.scalar resolver_key
22+
yaml.scalar source
23+
dependency.requirement.to_yaml(yaml)
24+
end
25+
end
26+
27+
# Parse a dependency from a CLI argument
28+
def self.from_cli(value : String) : DependencyDefinition
29+
parts = parts_from_cli(value)
30+
31+
# We need to check the actual shard name to create a dependency.
32+
# This requires getting the actual spec file from some matching version.
33+
resolver = Resolver.find_resolver(parts.resolver_key, "unknown", parts.source)
34+
version = resolver.versions_for(parts.requirement).first || raise Shards::Error.new("No versions found for dependency: #{value}")
35+
spec = resolver.spec(version)
36+
name = spec.name || raise Shards::Error.new("No name found for dependency: #{value}")
37+
38+
DependencyDefinition.new(Dependency.new(name, resolver, parts.requirement), parts.resolver_key, parts.source)
39+
end
40+
41+
# :nodoc:
42+
#
43+
# Parse the dependency from a CLI argument
44+
# and return the parts needed to create the proper dependency.
45+
#
46+
# Split to allow better unit testing.
47+
def self.parts_from_cli(value : String) : Parts
48+
resolver_key = nil
49+
source = ""
50+
requirement = Any
51+
52+
if value.starts_with?("file://")
53+
resolver_key = "path"
54+
source = value[7..-1] # drop "file://"
55+
end
56+
57+
# relative paths
58+
if value.starts_with?("./") || value.starts_with?("../") || value.starts_with?(".\\") || value.starts_with?("..\\")
59+
resolver_key = "path"
60+
source = value
61+
end
62+
63+
if value.starts_with?("https://github.com")
64+
resolver_key = "github"
65+
uri = URI.parse(value)
66+
source = uri.path[1..-1].rchop(".git") # drop first "/""
67+
end
68+
69+
if value.starts_with?("https://gitlab.com")
70+
resolver_key = "gitlab"
71+
uri = URI.parse(value)
72+
source = uri.path[1..-1].rchop(".git") # drop first "/""
73+
end
74+
75+
if value.starts_with?("https://bitbucket.com")
76+
resolver_key = "bitbucket"
77+
uri = URI.parse(value)
78+
source = uri.path[1..-1] # drop first "/""
79+
end
80+
81+
if value.starts_with?("git://")
82+
resolver_key = "git"
83+
source = value
84+
end
85+
86+
if value.starts_with?("git@")
87+
resolver_key = "git"
88+
source = value
89+
end
90+
91+
unless resolver_key
92+
Resolver.resolver_keys.each do |key|
93+
key_schema = "#{key}:"
94+
if value.starts_with?(key_schema)
95+
resolver_key = key
96+
source = value.sub(key_schema, "")
97+
98+
# narrow down requirement
99+
if source.includes?("@")
100+
source, version = source.split("@")
101+
requirement = VersionReq.new("~> #{version}")
102+
end
103+
104+
break
105+
end
106+
end
107+
end
108+
109+
raise Shards::Error.new("Invalid dependency format: #{value}") unless resolver_key
110+
111+
Parts.new(resolver_key: resolver_key, source: source, requirement: requirement)
112+
end
113+
end
114+
end

0 commit comments

Comments
 (0)