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

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.
@@ -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