shivers 0.6.0.pre.2 → 0.6.0.pre.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../value_equality'
4
+
5
+ module Shivers
6
+ module Parts
7
+ class Static
8
+ include ValueEquality
9
+
10
+ def initialize(data)
11
+ @value = data[:value]
12
+ end
13
+
14
+ def matcher
15
+ /#{Regexp.quote(@value)}/
16
+ end
17
+
18
+ def convert(value)
19
+ value
20
+ end
21
+
22
+ def merge(_, second)
23
+ second
24
+ end
25
+
26
+ def capturable?
27
+ false
28
+ end
29
+
30
+ def multivalued?
31
+ false
32
+ end
33
+
34
+ def state
35
+ [@value]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'parts/numeric'
4
+ require_relative 'parts/alphanumeric'
5
+ require_relative 'parts/alphanumeric_or_hyphen'
6
+ require_relative 'parts/static'
7
+
8
+ module Shivers
9
+ module Parts
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shivers
4
+ module ValueEquality
5
+ def ==(other)
6
+ other.class == self.class && other.state == state
7
+ end
8
+
9
+ alias eql? ==
10
+
11
+ def hash
12
+ self.class.hash ^ state.hash
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Shivers
2
- VERSION = '0.6.0.pre.2'
4
+ class Version
5
+ def self.from_file(path)
6
+ git_sha = ENV['GIT_SHA'] || 'LOCAL'
7
+ metadata = git_sha.to_s
8
+
9
+ base_version =
10
+ if File.exist?(path)
11
+ File.open(path) { |file| file.read.strip }
12
+ else
13
+ '0.0.0'
14
+ end
15
+
16
+ Version.new("#{base_version}+#{metadata}")
17
+ end
18
+
19
+ def initialize(version_string)
20
+ @version = Semantic::Version.new(version_string)
21
+ end
22
+
23
+ def to_docker_tag
24
+ to_s.gsub(/\+/, '_').downcase
25
+ end
26
+
27
+ def to_s
28
+ @version.to_s
29
+ end
30
+ end
3
31
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'value_equality'
4
+
5
+ module Shivers
6
+ class Version2
7
+ include ValueEquality
8
+
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ def state
14
+ [@data]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ require_relative 'parts'
6
+ require_relative 'format'
7
+ require_relative 'visitors'
8
+ require_relative 'value_equality'
9
+
10
+ module Shivers
11
+ class VersionDefinition
12
+ PART_TYPES = {
13
+ numeric: Parts::Numeric,
14
+ alphanumeric: Parts::Alphanumeric,
15
+ alphanumeric_or_hyphen: Parts::AlphanumericOrHyphen,
16
+ static: Parts::Static
17
+ }.freeze
18
+
19
+ include ValueEquality
20
+
21
+ attr_reader :parts, :format
22
+
23
+ def initialize(definition)
24
+ @parts =
25
+ definition[:parts]
26
+ .transform_values { |part| PART_TYPES[part[:type]].new(part) }
27
+ @format = Format.new(definition[:formatter])
28
+ end
29
+
30
+ def parse(value)
31
+ extract_visitor = Visitors::ExtractVisitor.new(@parts, value)
32
+
33
+ @format.visit(extract_visitor)
34
+
35
+ Version2.new(
36
+ parts: @parts, format: @format,
37
+ values: extract_visitor.result
38
+ )
39
+ end
40
+
41
+ def state
42
+ [@parts, @format]
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ module Shivers
6
+ module Visitors
7
+ class ExtractVisitor
8
+ def initialize(parts, value, _options = {})
9
+ @parts = parts
10
+ @value = value
11
+ @delegate = MatcherVisitor.new(parts)
12
+ end
13
+
14
+ def optionally(&block)
15
+ @delegate.optionally(&block)
16
+ end
17
+
18
+ def recursively(name, &block)
19
+ @delegate.recursively(name, &block)
20
+ end
21
+
22
+ def method_missing(symbol, *args, &block)
23
+ @delegate.send(symbol, *args, &block)
24
+ end
25
+
26
+ def respond_to_missing?(symbol, include_private = false)
27
+ @delegate.respond_to_missing?(symbol, include_private) || super
28
+ end
29
+
30
+ def result
31
+ matchers = @delegate.result
32
+
33
+ parent_captures = match_parent(matchers)
34
+ total_captures = match_children(matchers, parent_captures)
35
+
36
+ convert_values(total_captures)
37
+ end
38
+
39
+ private
40
+
41
+ def match_parent(matchers)
42
+ standard_captures(/\A#{matchers.capturer.source}\z/, @value)
43
+ end
44
+
45
+ def match_children(matchers, captures)
46
+ matchers.children.reduce(captures) do |caps, child|
47
+ value = caps[child.capture_group]
48
+ first_captures = standard_captures(child.first, value)
49
+
50
+ next caps unless first_captures
51
+
52
+ rest_captures = recursive_captures(first_captures[:rest], child.rest)
53
+
54
+ caps = merge_captures(caps, first_captures)
55
+ caps = merge_captures(caps, rest_captures)
56
+
57
+ caps
58
+ end
59
+ end
60
+
61
+ def recursive_captures(value, matcher)
62
+ match_recursively(value, matcher)
63
+ .reduce({}) do |all_rest_captures, rest_captures|
64
+ all_rest_captures.merge(rest_captures) do |name, existing, new|
65
+ merge_capture_value(name, existing, new)
66
+ end
67
+ end
68
+ end
69
+
70
+ def match_recursively(value, matcher)
71
+ rest_matches = value.scan(matcher)
72
+ rest_capture_names = matcher.names.map(&:to_sym)
73
+ rest_matches.map do |rest_match|
74
+ rest_capture_names.zip(rest_match).to_h
75
+ end
76
+ end
77
+
78
+ def merge_captures(existing, new)
79
+ new.reduce(existing) do |captures, capture|
80
+ name, value = capture
81
+ captures.merge(
82
+ name =>
83
+ merge_capture_value(name, captures[name], value)
84
+ )
85
+ end
86
+ end
87
+
88
+ def merge_capture_value(name, existing, new)
89
+ @parts[name]&.merge(existing, new)
90
+ end
91
+
92
+ def standard_captures(matcher, value)
93
+ return unless value
94
+
95
+ ensure_match(matcher.match(value))
96
+ .named_captures
97
+ .transform_keys(&:to_sym)
98
+ end
99
+
100
+ def convert_values(captures)
101
+ capturable_parts
102
+ .to_h { |name, part| [name, part&.convert(captures[name])] }
103
+ end
104
+
105
+ def ensure_match(match)
106
+ unless match
107
+ raise(
108
+ ArgumentError,
109
+ "Version string: '#{@value}' does not satisfy expected format."
110
+ )
111
+ end
112
+
113
+ match
114
+ end
115
+
116
+ def capturable_parts
117
+ @parts.select { |_, part| part.capturable? }
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ require_relative '../matchers'
6
+ require_relative '../value_equality'
7
+
8
+ module Shivers
9
+ module Visitors
10
+ class MatcherVisitor
11
+ class RecursiveMatcherVisitor
12
+ def initialize(name, parts)
13
+ @name = name
14
+ @parts = parts
15
+ end
16
+
17
+ def first(&block)
18
+ @first_block = block
19
+ end
20
+
21
+ def rest(&block)
22
+ @rest_block = block
23
+ end
24
+
25
+ def result
26
+ first = visit(@first_block, MatcherVisitor.new(@parts))
27
+ rest = visit(@rest_block, MatcherVisitor.new(@parts))
28
+
29
+ Matchers::Child.new(
30
+ parent_matcher(first, rest), parent_capturer(first, rest),
31
+ Matchers::Recursive.new(
32
+ @name, first_capturer(first, rest), rest_capturer(first, rest)
33
+ )
34
+ )
35
+ end
36
+
37
+ private
38
+
39
+ def visit(block, visitor)
40
+ block.call(visitor)
41
+ visitor.result
42
+ end
43
+
44
+ def parent_matcher(first, rest)
45
+ /#{first.matcher.source}(?:#{rest.matcher.source})*/
46
+ end
47
+
48
+ def parent_capturer(first, rest)
49
+ /(?<#{@name}>#{first.matcher.source}(?:#{rest.matcher.source})*)/
50
+ end
51
+
52
+ def first_capturer(first, rest)
53
+ /#{first.capturer.source}(?<rest>(?:#{rest.matcher.source})*)?/
54
+ end
55
+
56
+ def rest_capturer(_, rest)
57
+ /#{rest.capturer.source}/
58
+ end
59
+ end
60
+
61
+ def initialize(parts)
62
+ @parts = parts
63
+ @matching_regexps = []
64
+ @capturing_regexps = []
65
+ @children = []
66
+ end
67
+
68
+ def optionally(&block)
69
+ sub_visitor = MatcherVisitor.new(@parts)
70
+ block.call(sub_visitor)
71
+ result = sub_visitor.result
72
+ @matching_regexps << /(?:#{result.matcher.source})?/
73
+ @capturing_regexps << /(?:#{result.capturer.source})?/
74
+ @children.concat(result.children)
75
+ end
76
+
77
+ def recursively(name, &block)
78
+ sub_visitor = RecursiveMatcherVisitor.new(name, @parts)
79
+ block.call(sub_visitor)
80
+ result = sub_visitor.result
81
+ @matching_regexps << result.matcher
82
+ @capturing_regexps << result.capturer
83
+ @children << result.child
84
+ end
85
+
86
+ def method_missing(symbol, *_)
87
+ raise no_dsl_element_error(symbol) unless respond_to_missing?(symbol)
88
+
89
+ part = @parts[symbol]
90
+
91
+ @matching_regexps << part.matcher
92
+ @capturing_regexps << if part.capturable?
93
+ /(?<#{symbol}>#{part.matcher.source})/
94
+ else
95
+ part.matcher
96
+ end
97
+ end
98
+
99
+ def respond_to_missing?(symbol, _ = false)
100
+ @parts.include?(symbol) || super
101
+ end
102
+
103
+ def result
104
+ Matchers::Parent.new(
105
+ /#{@matching_regexps.collect(&:source).join}/,
106
+ /#{@capturing_regexps.collect(&:source).join}/,
107
+ @children
108
+ )
109
+ end
110
+
111
+ private
112
+
113
+ def no_dsl_element_error(symbol)
114
+ NoMethodError.new(
115
+ "DSL does not include an element with name: '#{symbol}'. "\
116
+ 'Check usage.',
117
+ symbol
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'visitors/matcher_visitor'
4
+ require_relative 'visitors/extract_visitor'
5
+
6
+ module Shivers
7
+ module Visitors
8
+ end
9
+ end
data/lib/shivers.rb CHANGED
@@ -1,30 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shivers/library_version'
4
+ require 'shivers/value_equality'
5
+ require 'shivers/matchers'
1
6
  require 'shivers/version'
7
+ require 'shivers/version2'
8
+ require 'shivers/version_definition'
2
9
 
3
10
  require 'semantic'
4
-
5
- module Shivers
6
- class Version
7
- def self.from_file(path)
8
- git_sha = ENV['GIT_SHA'] || 'LOCAL'
9
- metadata = "#{git_sha}"
10
-
11
- base_version = File.exist?(path) ?
12
- File.open(path) { |file| file.read.strip } :
13
- '0.0.0'
14
-
15
- Version.new("#{base_version}+#{metadata}")
16
- end
17
-
18
- def initialize(version_string)
19
- @version = Semantic::Version.new(version_string)
20
- end
21
-
22
- def to_docker_tag
23
- to_s.gsub(/[\+]/, '_').downcase
24
- end
25
-
26
- def to_s
27
- @version.to_s
28
- end
29
- end
30
- end
data/shivers.gemspec CHANGED
@@ -1,7 +1,19 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'shivers/version'
5
+ require_relative 'lib/shivers/library_version'
6
+
7
+ files = %w[
8
+ bin
9
+ lib
10
+ CODE_OF_CONDUCT.md
11
+ shivers.gemspec
12
+ Gemfile
13
+ LICENSE.txt
14
+ Rakefile
15
+ README.md
16
+ ]
5
17
 
6
18
  Gem::Specification.new do |spec|
7
19
  spec.name = 'shivers'
@@ -9,31 +21,34 @@ Gem::Specification.new do |spec|
9
21
  spec.authors = ['InfraBlocks Maintainers']
10
22
  spec.email = ['maintainers@infrablocks.io']
11
23
 
12
- spec.date = '2021-01-11'
13
24
  spec.summary = 'Semantic version numbers for CI pipelines.'
14
- spec.description = 'Semantic version number management, by file and environment, for CI pipelines.'
25
+ spec.description = 'Semantic version number management, by file and '\
26
+ 'environment, for CI pipelines.'
15
27
  spec.homepage = 'https://github.com/infrablocks/shivers'
16
28
  spec.license = 'MIT'
17
29
 
18
30
  spec.files = `git ls-files -z`.split("\x0").select do |f|
19
- f.match(%r{^(bin|lib|CODE_OF_CONDUCT\.md|shivers\.gemspec|Gemfile|LICENSE\.txt|Rakefile|README\.md)})
31
+ f.match(/^(#{files.map { |g| Regexp.escape(g) }.join('|')})/)
20
32
  end
21
33
  spec.bindir = 'exe'
22
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
35
  spec.require_paths = ['lib']
24
36
 
25
- spec.required_ruby_version = '>= 2.6'
37
+ spec.required_ruby_version = '>= 2.7'
26
38
 
27
39
  spec.add_dependency 'semantic', '~> 1.6'
28
40
 
29
- spec.add_development_dependency 'bundler', '~> 2.0'
30
- spec.add_development_dependency 'rake', '~> 13.0'
31
- spec.add_development_dependency 'rake_circle_ci', '~> 0.9'
32
- spec.add_development_dependency 'rake_github', '~> 0.7'
33
- spec.add_development_dependency 'rake_ssh', '~> 0.6'
34
- spec.add_development_dependency 'rake_gpg', '~> 0.14'
35
- spec.add_development_dependency 'rspec', '~> 3.9'
36
- spec.add_development_dependency 'fakefs', '~> 0.18'
37
- spec.add_development_dependency 'gem-release', '~> 2.0'
38
- spec.add_development_dependency 'simplecov', '~> 0.16'
41
+ spec.add_development_dependency 'bundler'
42
+ spec.add_development_dependency 'fakefs'
43
+ spec.add_development_dependency 'gem-release'
44
+ spec.add_development_dependency 'rake'
45
+ spec.add_development_dependency 'rake_circle_ci'
46
+ spec.add_development_dependency 'rake_github'
47
+ spec.add_development_dependency 'rake_gpg'
48
+ spec.add_development_dependency 'rake_ssh'
49
+ spec.add_development_dependency 'rspec'
50
+ spec.add_development_dependency 'rubocop'
51
+ spec.add_development_dependency 'rubocop-rake'
52
+ spec.add_development_dependency 'rubocop-rspec'
53
+ spec.add_development_dependency 'simplecov'
39
54
  end