ast-template 7.1.9 → 7.1.11
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/ast/template/file_types.rb +50 -0
- data/lib/ast/template/merge_dispatch.rb +74 -0
- data/lib/ast/template/rust_host_provider.rb +53 -0
- data/lib/ast/template/strategies.rb +92 -0
- data/lib/ast/template/token_application.rb +71 -0
- data/lib/ast/template/typed_core_bridge.rb +121 -0
- data/lib/ast/template/version.rb +1 -1
- data/lib/ast/template.rb +9 -0
- data.tar.gz.sig +0 -0
- metadata +47 -21
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d333d88ffe0b32f1cf3bce7401deaaad28eaca1f40defae1d787e3fe782c6ebd
|
|
4
|
+
data.tar.gz: 903c10dc9ad0b5825ade7d2cd41fc0d9a2b0722b1ee0be425d41a5d70efe5c52
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 618776085a1f8680cba8e8640149129a71c8d9589a08c6a0536bef146a15d38db0fb5832b450d596d2368dc6a30c198be22f2e2b2c57a8e8ffa5a23343e8e991
|
|
7
|
+
data.tar.gz: f86daa1f153f431e3b27c8fd0fb9d63a4cf5f9af1657a6ea88116361e89db7de473e3e46d743d4bcd0e324be8a04b382977dcbe2b5816a9511402307b7ecdd84
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ast
|
|
4
|
+
module Template
|
|
5
|
+
# Destination-path file-type detection. Contract: structuredmerge/spec
|
|
6
|
+
# slice-1043, fixtures at
|
|
7
|
+
# structuredmerge/fixtures/templating/slice-1043-merge-dispatch.
|
|
8
|
+
#
|
|
9
|
+
# First match wins; the rule order is load-bearing (e.g. `.gemfile`
|
|
10
|
+
# shadows the appraisals rule for `Appraisal.root.gemfile` — preserved
|
|
11
|
+
# from kettle-jem's effective behavior and documented in the slice).
|
|
12
|
+
module FileTypes
|
|
13
|
+
RUBY_BASENAMES = %w[Gemfile Rakefile Appraisals Appraisal.root.gemfile .simplecov].freeze
|
|
14
|
+
RUBY_SUFFIXES = %w[.gemspec .gemfile].freeze
|
|
15
|
+
RUBY_EXTENSIONS = %w[.rb .rake].freeze
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# @param path [String] logical destination path
|
|
20
|
+
# @param configured [String, Symbol, nil] destination-config override
|
|
21
|
+
# @return [Symbol] the file type
|
|
22
|
+
def detect(path, configured: nil)
|
|
23
|
+
return configured.to_sym unless configured.to_s.empty?
|
|
24
|
+
|
|
25
|
+
relative_path = path.to_s
|
|
26
|
+
basename = File.basename(relative_path)
|
|
27
|
+
extension = File.extname(relative_path).downcase
|
|
28
|
+
return :gitignore if basename == ".gitignore"
|
|
29
|
+
return :gemfile if basename == "Gemfile" || basename.end_with?(".gemfile")
|
|
30
|
+
return :appraisals if basename.start_with?("Appraisals")
|
|
31
|
+
return :gemspec if basename.end_with?(".gemspec")
|
|
32
|
+
return :rakefile if basename == "Rakefile" || extension == ".rake"
|
|
33
|
+
return :ruby if RUBY_BASENAMES.include?(basename) ||
|
|
34
|
+
RUBY_SUFFIXES.any? { |suffix| basename.end_with?(suffix) } ||
|
|
35
|
+
RUBY_EXTENSIONS.include?(extension)
|
|
36
|
+
return :yaml if extension.match?(/\A\.ya?ml\z/) || basename.casecmp("citation.cff").zero?
|
|
37
|
+
return :toml if extension == ".toml"
|
|
38
|
+
return :jsonc if relative_path == ".devcontainer/devcontainer.json"
|
|
39
|
+
return :jsonc if extension == ".jsonc"
|
|
40
|
+
return :json5 if extension == ".json5"
|
|
41
|
+
return :json if extension == ".json"
|
|
42
|
+
return :markdown if extension.match?(/\A\.md(?:own)?\z/)
|
|
43
|
+
return :dotenv if basename.start_with?(".env") || basename.end_with?(".env") || extension == ".env"
|
|
44
|
+
return :rbs if extension == ".rbs"
|
|
45
|
+
|
|
46
|
+
:text
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ast
|
|
4
|
+
module Template
|
|
5
|
+
# Merge dispatch: route a file type to its merger adapter. Contract:
|
|
6
|
+
# structuredmerge/spec slice-1043, fixtures at
|
|
7
|
+
# structuredmerge/fixtures/templating/slice-1043-merge-dispatch.
|
|
8
|
+
#
|
|
9
|
+
# Default adapters wrap the ast-merge family gems and are required LAZILY —
|
|
10
|
+
# ast-template does not hard-depend on merger gems; the consumer's bundle
|
|
11
|
+
# provides them. Types needing consumer policy (gemspec, the Ruby family,
|
|
12
|
+
# json variants, dotenv, rbs, bash, gitignore) have no default adapter —
|
|
13
|
+
# register one.
|
|
14
|
+
module MergeDispatch
|
|
15
|
+
Adapter = Struct.new(:gem_name, :require_path, :invoke)
|
|
16
|
+
|
|
17
|
+
DEFAULT_ADAPTERS = {
|
|
18
|
+
yaml: Adapter.new("psych-merge", "psych/merge", ->(t, d, **opts) { Psych::Merge.merge_yaml(t, d, "yaml", **opts) }),
|
|
19
|
+
toml: Adapter.new("citrus-toml-merge", "citrus/toml/merge", ->(t, d, **opts) { Citrus::Toml::Merge.merge_toml(t, d, "toml", **opts) }),
|
|
20
|
+
markdown: Adapter.new("markdown-merge", "markdown/merge", ->(t, d, **opts) { Markdown::Merge.merge_markdown(t, d, "markdown", **opts) })
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
@adapters = {}
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# Register an adapter for a file type. The callable receives
|
|
27
|
+
# (template_content, destination_content, **options) and returns the
|
|
28
|
+
# merger result hash ({ok:, output:, diagnostics:}).
|
|
29
|
+
def register(file_type, callable = nil, &block)
|
|
30
|
+
adapter = callable || block
|
|
31
|
+
raise ArgumentError, "adapter callable required" unless adapter.respond_to?(:call)
|
|
32
|
+
|
|
33
|
+
adapters[file_type.to_sym] = adapter
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Whether any adapter (registered or default) exists for the type.
|
|
37
|
+
def registered?(file_type)
|
|
38
|
+
adapters.key?(file_type.to_sym) || DEFAULT_ADAPTERS.key?(file_type.to_sym)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Dispatch to the type's adapter. Returns nil when no adapter exists —
|
|
42
|
+
# the caller's fallback is the template content.
|
|
43
|
+
#
|
|
44
|
+
# @return [Hash, nil] the merger result, or nil when unregistered
|
|
45
|
+
def dispatch(file_type, template_content, destination_content, **options)
|
|
46
|
+
type = file_type.to_sym
|
|
47
|
+
registered = adapters[type]
|
|
48
|
+
return registered.call(template_content, destination_content, **options) if registered
|
|
49
|
+
|
|
50
|
+
default = DEFAULT_ADAPTERS[type]
|
|
51
|
+
return nil unless default
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
require default.require_path
|
|
55
|
+
rescue LoadError
|
|
56
|
+
raise LoadError, "merge dispatch for #{type} requires #{default.gem_name}; add it to the consumer bundle"
|
|
57
|
+
end
|
|
58
|
+
default.invoke.call(template_content, destination_content, **options)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Test support: clear consumer-registered adapters.
|
|
62
|
+
def reset!
|
|
63
|
+
@adapters = {}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def adapters
|
|
69
|
+
@adapters ||= {}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'typed_core_bridge'
|
|
4
|
+
|
|
5
|
+
module Ast
|
|
6
|
+
module Template
|
|
7
|
+
# Explicit report-only bridge for the portable Rust session contract.
|
|
8
|
+
# Template execution and filesystem mutation remain Ruby-owned.
|
|
9
|
+
class RustHostProvider
|
|
10
|
+
include TypedCoreBridge
|
|
11
|
+
PROVIDER_ID = 'rust.ast-template.session-reports'
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def available?
|
|
15
|
+
require 'structuredmerge_core' unless defined?(::StructuredmergeCore)
|
|
16
|
+
%i[report_template_options report_template_profile plan_template_directory].all? do |name|
|
|
17
|
+
::StructuredmergeCore.respond_to?(name)
|
|
18
|
+
end
|
|
19
|
+
rescue LoadError
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def provider_id = PROVIDER_ID
|
|
25
|
+
|
|
26
|
+
def capabilities
|
|
27
|
+
{
|
|
28
|
+
operations: %i[options_report profile_report plan_report],
|
|
29
|
+
backend: :rust_tslp,
|
|
30
|
+
execution: :report_and_plan_only,
|
|
31
|
+
source_projection: :ruby_owned
|
|
32
|
+
}.freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def report(request)
|
|
36
|
+
core_report(request)
|
|
37
|
+
rescue KeyError, ArgumentError, TypeError => e
|
|
38
|
+
raise e.message.to_s
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def plan(request)
|
|
42
|
+
report(request.merge(kind: 'plan'))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def host
|
|
48
|
+
require 'structuredmerge_core' unless defined?(::StructuredmergeCore)
|
|
49
|
+
::StructuredmergeCore
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ast
|
|
4
|
+
module Template
|
|
5
|
+
# Strategy resolution and application decisions for template entries.
|
|
6
|
+
# Contract: structuredmerge/spec slice-1042, conformance fixtures at
|
|
7
|
+
# structuredmerge/fixtures/templating/slice-1042-strategy-application.
|
|
8
|
+
#
|
|
9
|
+
# The vocabulary (execution_action / write_action / status) matches the
|
|
10
|
+
# slice-353 session transport fixtures. The merger itself is the next
|
|
11
|
+
# contract surface (slice-1043); `merge` on differing content stops at
|
|
12
|
+
# the decision here.
|
|
13
|
+
module Strategies
|
|
14
|
+
# The four strategy names a destination config may declare.
|
|
15
|
+
STRATEGIES = %w[merge keep_destination raw_copy accept_template].freeze
|
|
16
|
+
DEFAULT_STRATEGY = "merge"
|
|
17
|
+
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
# Effective strategy for a destination path: the matching override wins,
|
|
21
|
+
# else the default. Overrides are TemplateStrategyOverride-shaped
|
|
22
|
+
# entries ({path:, strategy:} with string or symbol keys).
|
|
23
|
+
#
|
|
24
|
+
# @param default [String] strategy when no override matches
|
|
25
|
+
# @param overrides [Array<Hash>] per-path strategy overrides
|
|
26
|
+
# @param path [String] logical destination path
|
|
27
|
+
# @return [String] the effective strategy
|
|
28
|
+
def resolve(default: DEFAULT_STRATEGY, overrides: [], path:)
|
|
29
|
+
override = Array(overrides).find { |entry| (entry[:path] || entry["path"]) == path }
|
|
30
|
+
strategy = override && (override[:strategy] || override["strategy"])
|
|
31
|
+
strategy.to_s.empty? ? default : strategy.to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The decision for one entry. Content is present only when this contract
|
|
35
|
+
# owns it (raw_copy, accept_template, merge-with-empty-destination);
|
|
36
|
+
# keep decisions and merge-of-differences return no content (the latter
|
|
37
|
+
# defers to the merger).
|
|
38
|
+
#
|
|
39
|
+
# @param strategy [String] resolved strategy for the entry
|
|
40
|
+
# @param template_content [String] prepared template content
|
|
41
|
+
# @param destination_content [String, nil] current destination content
|
|
42
|
+
# (nil when the destination file is absent)
|
|
43
|
+
# @return [Hash] {execution_action:, write_action:, status:, content:}
|
|
44
|
+
def plan_entry(strategy:, template_content:, destination_content:)
|
|
45
|
+
strategy = strategy.to_s
|
|
46
|
+
return keep_decision if strategy == "keep_destination"
|
|
47
|
+
|
|
48
|
+
absent = destination_content.nil?
|
|
49
|
+
if strategy == "merge"
|
|
50
|
+
trivial = trivial_merge_outcome(template_content, destination_content)
|
|
51
|
+
return keep_decision if trivial == :identical
|
|
52
|
+
return write_decision("merge_prepared_content", absent, template_content) if trivial == :empty_destination
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# accept_template and merge-of-differences both write prepared content;
|
|
56
|
+
# for merge-of-differences the content is the merger's, not ours.
|
|
57
|
+
content = (strategy == "merge") ? nil : template_content
|
|
58
|
+
write_decision(execution_action_for(strategy), absent, content)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Whether the strategy leaves the destination untouched. Absent
|
|
62
|
+
# destinations are not created under keep_destination.
|
|
63
|
+
def keep_destination?(strategy)
|
|
64
|
+
strategy.to_s == "keep_destination"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The merge short-circuits that never invoke a merger:
|
|
68
|
+
# :empty_destination when there is nothing to merge into (template
|
|
69
|
+
# becomes the content), :identical when destination already equals
|
|
70
|
+
# template (destination is kept), nil when a real merge is required.
|
|
71
|
+
def trivial_merge_outcome(template_content, destination_content)
|
|
72
|
+
return :empty_destination if destination_content.to_s.strip.empty?
|
|
73
|
+
return :identical if destination_content == template_content
|
|
74
|
+
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def keep_decision
|
|
79
|
+
{execution_action: "keep", write_action: "keep", status: "keep", content: nil}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def write_decision(execution_action, create, content)
|
|
83
|
+
action = create ? "create" : "update"
|
|
84
|
+
{execution_action: execution_action, write_action: action, status: action, content: content}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def execution_action_for(strategy)
|
|
88
|
+
(strategy == "raw_copy") ? "raw_copy" : "merge_prepared_content"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'token/resolver'
|
|
4
|
+
|
|
5
|
+
module Ast
|
|
6
|
+
module Template
|
|
7
|
+
# Token application policy: normalize replacements, resolve with
|
|
8
|
+
# on-missing keep, then police tokens that survive in the consumer's
|
|
9
|
+
# namespace. Token mechanics (parsing, grammar) live in token-resolver;
|
|
10
|
+
# this module is the contract surface defined by
|
|
11
|
+
# structuredmerge/spec slice-1041 and its conformance fixtures.
|
|
12
|
+
module TokenApplication
|
|
13
|
+
# Raised when resolved output still contains tokens in the consumer's
|
|
14
|
+
# namespace. Carries the sorted, deduplicated canonical full keys so
|
|
15
|
+
# consumers can re-raise with their own branded message.
|
|
16
|
+
class UnresolvedTokensError < ArgumentError
|
|
17
|
+
# @return [Array<String>] canonical full token keys, sorted
|
|
18
|
+
attr_reader :tokens
|
|
19
|
+
|
|
20
|
+
def initialize(tokens)
|
|
21
|
+
@tokens = tokens
|
|
22
|
+
super("unresolved template tokens: #{tokens.map { |token| "{#{token}}" }.join(', ')}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# Resolve tokens in content, then (when scanning) raise if any token in
|
|
29
|
+
# the consumer's namespace survived. Missing replacements are kept
|
|
30
|
+
# verbatim — in canonical form, so the scan sees canonical keys.
|
|
31
|
+
#
|
|
32
|
+
# @param content [String] template content
|
|
33
|
+
# @param replacements [Hash] token canonical key => value (keys and
|
|
34
|
+
# values are stringified before resolution)
|
|
35
|
+
# @param config [Token::Resolver::Config] token structure config
|
|
36
|
+
# @param namespace [String] first-segment namespace the consumer owns
|
|
37
|
+
# @param scan_unresolved [Boolean] when false, unresolved tokens pass
|
|
38
|
+
# @return [String] resolved content
|
|
39
|
+
# @raise [UnresolvedTokensError] unresolved namespaced tokens remain
|
|
40
|
+
def apply_tokens(content, replacements, config:, namespace:, scan_unresolved: true)
|
|
41
|
+
document = Token::Resolver::Document.new(content.to_s, config: config)
|
|
42
|
+
resolved = Token::Resolver::Resolve.new(on_missing: :keep).resolve(document, stringify_replacements(replacements))
|
|
43
|
+
return resolved unless scan_unresolved
|
|
44
|
+
|
|
45
|
+
unresolved = unresolved_namespaced_tokens(resolved, config: config, namespace: namespace)
|
|
46
|
+
return resolved if unresolved.empty?
|
|
47
|
+
|
|
48
|
+
raise UnresolvedTokensError, unresolved
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Sorted, deduplicated canonical keys of tokens in content whose first
|
|
52
|
+
# segment equals namespace.
|
|
53
|
+
#
|
|
54
|
+
# @return [Array<String>]
|
|
55
|
+
def unresolved_namespaced_tokens(content, config:, namespace:)
|
|
56
|
+
Token::Resolver::Document.new(content.to_s, config: config)
|
|
57
|
+
.token_keys
|
|
58
|
+
.select { |key| key.split(config.separators.first, 2).first == namespace }
|
|
59
|
+
.uniq
|
|
60
|
+
.sort
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Normalize replacements for the resolver: string keys and values.
|
|
64
|
+
#
|
|
65
|
+
# @return [Hash{String => String}]
|
|
66
|
+
def stringify_replacements(replacements)
|
|
67
|
+
replacements.to_h.transform_keys(&:to_s).transform_values(&:to_s)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ast
|
|
4
|
+
module Template
|
|
5
|
+
# Converts transport shapes only; configuration resolution and planning
|
|
6
|
+
# remain in Rust. No Ruby selection, template execution or writes occur here.
|
|
7
|
+
module TypedCoreBridge
|
|
8
|
+
REPORT_FIELDS = {
|
|
9
|
+
'TemplateSessionRequestReport' => %w[request_kind profile_name mode ready diagnostics resolved_options],
|
|
10
|
+
'TemplateSessionOptions' => %w[mode template_root destination_root context default_strategy overrides
|
|
11
|
+
replacements allowed_families config],
|
|
12
|
+
'TemplateDestinationContext' => %w[project_name],
|
|
13
|
+
'TemplateStrategyOverride' => %w[path strategy],
|
|
14
|
+
'TemplateTokenConfig' => %w[pre post separators min_segments max_segments segment_pattern],
|
|
15
|
+
'SessionDiagnostic' => %w[severity category reason path family message],
|
|
16
|
+
'TemplateSessionPlanReport' => %w[mode runner_report],
|
|
17
|
+
'TemplateDirectoryRunnerReport' => %w[plan_report preview run_report apply_report],
|
|
18
|
+
'TemplateDirectoryPlanReport' => %w[entries summary],
|
|
19
|
+
'TemplateDirectoryPlanReportEntry' => %w[template_source_path logical_destination_path destination_path
|
|
20
|
+
execution_action write_action status previewable],
|
|
21
|
+
'TemplateDirectoryPlanReportSummary' => %w[create update keep blocked omitted],
|
|
22
|
+
'TemplatePreviewResult' => %w[result_files created_paths updated_paths kept_paths blocked_paths omitted_paths]
|
|
23
|
+
}.freeze
|
|
24
|
+
OMIT_NIL = {
|
|
25
|
+
'TemplateSessionRequestReport' => %w[profile_name],
|
|
26
|
+
'TemplateSessionOptions' => %w[config],
|
|
27
|
+
'TemplateDestinationContext' => %w[project_name],
|
|
28
|
+
'TemplateTokenConfig' => %w[max_segments],
|
|
29
|
+
'SessionDiagnostic' => %w[path family]
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def core_report(request)
|
|
35
|
+
request = require_hash(normalize_transport(request))
|
|
36
|
+
options = template_options(request.fetch('options'))
|
|
37
|
+
result = case request.fetch('kind')
|
|
38
|
+
when 'options'
|
|
39
|
+
host.report_template_options(options)
|
|
40
|
+
when 'profile'
|
|
41
|
+
profiles = require_hash(request.fetch('profiles', {})).transform_values do |profile|
|
|
42
|
+
template_profile(profile)
|
|
43
|
+
end
|
|
44
|
+
host.report_template_profile(host::TemplateProfileRequest.new(
|
|
45
|
+
profile_name: request.fetch('profile_name'), profiles: profiles, options: options
|
|
46
|
+
))
|
|
47
|
+
when 'plan'
|
|
48
|
+
host.plan_template_directory(options)
|
|
49
|
+
else
|
|
50
|
+
raise ArgumentError, "unsupported ast-template report kind #{request['kind'].inspect}"
|
|
51
|
+
end
|
|
52
|
+
project_report(result).transform_keys(&:to_sym)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def normalize_transport(value)
|
|
56
|
+
case value
|
|
57
|
+
when Hash then value.to_h { |key, item| [key.to_s, normalize_transport(item)] }
|
|
58
|
+
when Array then value.map { |item| normalize_transport(item) }
|
|
59
|
+
when Symbol then value.to_s
|
|
60
|
+
else value
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def template_fields(value)
|
|
65
|
+
value = require_hash(value)
|
|
66
|
+
fields = %w[mode default_strategy replacements].to_h { |field| [field.to_sym, value.fetch(field)] }
|
|
67
|
+
fields[:replacements] = require_hash(fields[:replacements])
|
|
68
|
+
fields[:context] =
|
|
69
|
+
host::TemplateDestinationContext.new(project_name: require_hash(value.fetch('context'))['project_name'])
|
|
70
|
+
overrides = value.fetch('overrides')
|
|
71
|
+
raise TypeError, 'template overrides must be an array' unless overrides.is_a?(Array)
|
|
72
|
+
|
|
73
|
+
fields[:overrides] = overrides.map do |entry|
|
|
74
|
+
entry = require_hash(entry)
|
|
75
|
+
host::TemplateStrategyOverride.new(path: entry.fetch('path'), strategy: entry.fetch('strategy'))
|
|
76
|
+
end
|
|
77
|
+
fields[:allowed_families] = value['allowed_families']
|
|
78
|
+
fields[:config] = unless value['config'].nil?
|
|
79
|
+
config = require_hash(value['config'])
|
|
80
|
+
args = %w[pre post separators min_segments segment_pattern].to_h do |key|
|
|
81
|
+
[key.to_sym, config.fetch(key)]
|
|
82
|
+
end
|
|
83
|
+
host::TemplateTokenConfig.new(**args, max_segments: config['max_segments'])
|
|
84
|
+
end
|
|
85
|
+
fields
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def require_hash(value)
|
|
89
|
+
raise TypeError, 'template request fields must be objects' unless value.is_a?(Hash)
|
|
90
|
+
|
|
91
|
+
value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def template_options(value)
|
|
95
|
+
host::TemplateSessionOptions.new(**template_fields(value),
|
|
96
|
+
template_root: value.fetch('template_root'), destination_root: value.fetch('destination_root'))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def template_profile(value)
|
|
100
|
+
host::DirectorySessionProfile.new(**template_fields(value))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def project_report(value)
|
|
104
|
+
case value
|
|
105
|
+
when Hash then value.transform_values { |item| project_report(item) }
|
|
106
|
+
when Array then value.map { |item| project_report(item) }
|
|
107
|
+
when Symbol then value.to_s
|
|
108
|
+
when NilClass, String, Numeric, TrueClass, FalseClass then value
|
|
109
|
+
else
|
|
110
|
+
type = value.class.name.delete_prefix('StructuredmergeCore::')
|
|
111
|
+
REPORT_FIELDS.fetch(type).each_with_object({}) do |field, result|
|
|
112
|
+
item = value.public_send(field)
|
|
113
|
+
next if item.nil? && OMIT_NIL.fetch(type, []).include?(field)
|
|
114
|
+
|
|
115
|
+
result[field] = project_report(item)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/ast/template/version.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Ast
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = '7.1.
|
|
8
|
+
VERSION = '7.1.11'
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
data/lib/ast/template.rb
CHANGED
|
@@ -25,8 +25,17 @@ module Ast
|
|
|
25
25
|
'rust' => 'Rust',
|
|
26
26
|
'typescript' => 'TypeScript'
|
|
27
27
|
}.freeze
|
|
28
|
+
autoload :FileTypes, 'ast/template/file_types'
|
|
29
|
+
autoload :MergeDispatch, 'ast/template/merge_dispatch'
|
|
30
|
+
autoload :RustHostProvider, 'ast/template/rust_host_provider'
|
|
31
|
+
autoload :Strategies, 'ast/template/strategies'
|
|
32
|
+
autoload :TokenApplication, 'ast/template/token_application'
|
|
28
33
|
|
|
29
34
|
class << self
|
|
35
|
+
def rust_host_provider
|
|
36
|
+
@rust_host_provider ||= RustHostProvider.new
|
|
37
|
+
end
|
|
38
|
+
|
|
30
39
|
def readme_family_language_aliases(self_language, language_order = README_FAMILY_LANGUAGE_ORDER)
|
|
31
40
|
self_id = self_language.to_s
|
|
32
41
|
alternatives = language_order.map(&:to_s).reject { |language| language == self_id }
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ast-template
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.1.
|
|
4
|
+
version: 7.1.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -43,14 +43,34 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - '='
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 7.1.
|
|
46
|
+
version: 7.1.11
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - '='
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 7.1.
|
|
53
|
+
version: 7.1.11
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: token-resolver
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '2.0'
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: 2.0.10
|
|
64
|
+
type: :runtime
|
|
65
|
+
prerelease: false
|
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - "~>"
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '2.0'
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: 2.0.10
|
|
54
74
|
- !ruby/object:Gem::Dependency
|
|
55
75
|
name: version_gem
|
|
56
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -77,20 +97,20 @@ dependencies:
|
|
|
77
97
|
requirements:
|
|
78
98
|
- - "~>"
|
|
79
99
|
- !ruby/object:Gem::Version
|
|
80
|
-
version: '3.
|
|
100
|
+
version: '3.1'
|
|
81
101
|
- - ">="
|
|
82
102
|
- !ruby/object:Gem::Version
|
|
83
|
-
version: 3.
|
|
103
|
+
version: 3.1.8
|
|
84
104
|
type: :development
|
|
85
105
|
prerelease: false
|
|
86
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
87
107
|
requirements:
|
|
88
108
|
- - "~>"
|
|
89
109
|
- !ruby/object:Gem::Version
|
|
90
|
-
version: '3.
|
|
110
|
+
version: '3.1'
|
|
91
111
|
- - ">="
|
|
92
112
|
- !ruby/object:Gem::Version
|
|
93
|
-
version: 3.
|
|
113
|
+
version: 3.1.8
|
|
94
114
|
- !ruby/object:Gem::Dependency
|
|
95
115
|
name: bundler-audit
|
|
96
116
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -168,7 +188,7 @@ dependencies:
|
|
|
168
188
|
version: '3.2'
|
|
169
189
|
- - ">="
|
|
170
190
|
- !ruby/object:Gem::Version
|
|
171
|
-
version: 3.2.
|
|
191
|
+
version: 3.2.5
|
|
172
192
|
type: :development
|
|
173
193
|
prerelease: false
|
|
174
194
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -178,7 +198,7 @@ dependencies:
|
|
|
178
198
|
version: '3.2'
|
|
179
199
|
- - ">="
|
|
180
200
|
- !ruby/object:Gem::Version
|
|
181
|
-
version: 3.2.
|
|
201
|
+
version: 3.2.5
|
|
182
202
|
- !ruby/object:Gem::Dependency
|
|
183
203
|
name: kettle-test
|
|
184
204
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -188,7 +208,7 @@ dependencies:
|
|
|
188
208
|
version: '2.0'
|
|
189
209
|
- - ">="
|
|
190
210
|
- !ruby/object:Gem::Version
|
|
191
|
-
version: 2.0.
|
|
211
|
+
version: 2.0.23
|
|
192
212
|
type: :development
|
|
193
213
|
prerelease: false
|
|
194
214
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -198,7 +218,7 @@ dependencies:
|
|
|
198
218
|
version: '2.0'
|
|
199
219
|
- - ">="
|
|
200
220
|
- !ruby/object:Gem::Version
|
|
201
|
-
version: 2.0.
|
|
221
|
+
version: 2.0.23
|
|
202
222
|
- !ruby/object:Gem::Dependency
|
|
203
223
|
name: turbo_tests2
|
|
204
224
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -208,7 +228,7 @@ dependencies:
|
|
|
208
228
|
version: '3.2'
|
|
209
229
|
- - ">="
|
|
210
230
|
- !ruby/object:Gem::Version
|
|
211
|
-
version: 3.2.
|
|
231
|
+
version: 3.2.14
|
|
212
232
|
type: :development
|
|
213
233
|
prerelease: false
|
|
214
234
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -218,7 +238,7 @@ dependencies:
|
|
|
218
238
|
version: '3.2'
|
|
219
239
|
- - ">="
|
|
220
240
|
- !ruby/object:Gem::Version
|
|
221
|
-
version: 3.2.
|
|
241
|
+
version: 3.2.14
|
|
222
242
|
- !ruby/object:Gem::Dependency
|
|
223
243
|
name: ruby-progressbar
|
|
224
244
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -242,7 +262,7 @@ dependencies:
|
|
|
242
262
|
version: '1.0'
|
|
243
263
|
- - ">="
|
|
244
264
|
- !ruby/object:Gem::Version
|
|
245
|
-
version: 1.0.
|
|
265
|
+
version: 1.0.9
|
|
246
266
|
type: :development
|
|
247
267
|
prerelease: false
|
|
248
268
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -252,7 +272,7 @@ dependencies:
|
|
|
252
272
|
version: '1.0'
|
|
253
273
|
- - ">="
|
|
254
274
|
- !ruby/object:Gem::Version
|
|
255
|
-
version: 1.0.
|
|
275
|
+
version: 1.0.9
|
|
256
276
|
- !ruby/object:Gem::Dependency
|
|
257
277
|
name: gitmoji-regex
|
|
258
278
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -262,7 +282,7 @@ dependencies:
|
|
|
262
282
|
version: '2.0'
|
|
263
283
|
- - ">="
|
|
264
284
|
- !ruby/object:Gem::Version
|
|
265
|
-
version: 2.0.
|
|
285
|
+
version: 2.0.15
|
|
266
286
|
type: :development
|
|
267
287
|
prerelease: false
|
|
268
288
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -272,7 +292,7 @@ dependencies:
|
|
|
272
292
|
version: '2.0'
|
|
273
293
|
- - ">="
|
|
274
294
|
- !ruby/object:Gem::Version
|
|
275
|
-
version: 2.0.
|
|
295
|
+
version: 2.0.15
|
|
276
296
|
description: "\U0001F48E Product-level directory session reporting above ast-merge
|
|
277
297
|
for Structured Merge."
|
|
278
298
|
email:
|
|
@@ -285,6 +305,12 @@ files:
|
|
|
285
305
|
- README.md
|
|
286
306
|
- lib/ast-template.rb
|
|
287
307
|
- lib/ast/template.rb
|
|
308
|
+
- lib/ast/template/file_types.rb
|
|
309
|
+
- lib/ast/template/merge_dispatch.rb
|
|
310
|
+
- lib/ast/template/rust_host_provider.rb
|
|
311
|
+
- lib/ast/template/strategies.rb
|
|
312
|
+
- lib/ast/template/token_application.rb
|
|
313
|
+
- lib/ast/template/typed_core_bridge.rb
|
|
288
314
|
- lib/ast/template/version.rb
|
|
289
315
|
- sig/ast/template.rbs
|
|
290
316
|
homepage: https://github.com/structuredmerge/structuredmerge-ruby
|
|
@@ -293,10 +319,10 @@ licenses:
|
|
|
293
319
|
- PolyForm-Small-Business-1.0.0
|
|
294
320
|
metadata:
|
|
295
321
|
homepage_uri: https://structuredmerge.org
|
|
296
|
-
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.
|
|
297
|
-
changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.
|
|
322
|
+
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.11
|
|
323
|
+
changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.11/CHANGELOG.md
|
|
298
324
|
bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
|
|
299
|
-
documentation_uri: https://www.rubydoc.info/gems/ast-template/7.1.
|
|
325
|
+
documentation_uri: https://www.rubydoc.info/gems/ast-template/7.1.11
|
|
300
326
|
funding_uri: https://github.com/sponsors/pboling
|
|
301
327
|
wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
|
|
302
328
|
news_uri: https://www.railsbling.com/tags/ast-template
|
|
@@ -327,7 +353,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
327
353
|
- !ruby/object:Gem::Version
|
|
328
354
|
version: '0'
|
|
329
355
|
requirements: []
|
|
330
|
-
rubygems_version: 4.0.
|
|
356
|
+
rubygems_version: 4.0.20
|
|
331
357
|
specification_version: 4
|
|
332
358
|
summary: "\U0001F48E Structured Merge template orchestration contracts for the Ruby
|
|
333
359
|
monorepo"
|
metadata.gz.sig
CHANGED
|
Binary file
|