ecg 0.2.0 → 0.3.0
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
- data/bin/ecg +1 -1
- data/lib/ecg/command.rb +8 -6
- data/lib/ecg/context.rb +29 -24
- data/lib/ecg/option_parser.rb +27 -89
- data/lib/ecg/plugin/load_file_values.rb +39 -0
- data/lib/ecg/plugin/load_option_values.rb +76 -0
- data/lib/ecg/store.rb +37 -19
- data/lib/ecg/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 260ccf7bd2c398c65719d3f5f9b58ca7665a50921440e05a3be2937873f8b2d0
|
4
|
+
data.tar.gz: e98ff38808434920d59cd1d1ce9b32e8dab5e3df756f6a32dbbed8db3ef30bd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 560ea77d18fcc83091068017084cdc86e4b6ae660dfe8a2f4d703e38ef2c561a073b5560a7b57ea27d4b39a998fecc2927df3255397b0db859f9c231a163437c
|
7
|
+
data.tar.gz: 473ceabbce68c12f01e99499bcfe128815fcbbfa502bd6a4d5dc6fea760875ec48cce840f7704a38a8f25af89e0d6302144be49d278ee9962dd2fd6b6f0d65e0
|
data/bin/ecg
CHANGED
data/lib/ecg/command.rb
CHANGED
@@ -5,16 +5,18 @@ require_relative 'option_parser'
|
|
5
5
|
|
6
6
|
module ECG
|
7
7
|
class Command
|
8
|
-
def initialize
|
9
|
-
@
|
8
|
+
def initialize
|
9
|
+
@context = Context.instance
|
10
10
|
end
|
11
11
|
|
12
|
-
def execute
|
13
|
-
context
|
12
|
+
def execute
|
13
|
+
@context.parser.parse!
|
14
14
|
# NOTE: ERB.new's non-keyword arguments are deprecated in 2.6
|
15
15
|
# erb = ERB.new(input.read, trim_mode: context.trim_mode)
|
16
|
-
|
17
|
-
|
16
|
+
@context.targets.each do |input, output|
|
17
|
+
erb = ERB.new(input.read, nil, @context.trim_mode)
|
18
|
+
output.puts erb.result(@context.binding)
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
data/lib/ecg/context.rb
CHANGED
@@ -1,42 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'singleton'
|
3
4
|
require_relative 'store'
|
5
|
+
require_relative 'plugin/load_file_values'
|
6
|
+
require_relative 'plugin/load_option_values'
|
4
7
|
|
5
8
|
module ECG
|
6
9
|
class Context
|
7
|
-
|
10
|
+
include Singleton
|
11
|
+
prepend Plugin::LoadFileValues
|
12
|
+
prepend Plugin::LoadOptionValues
|
8
13
|
|
9
|
-
|
10
|
-
@trim_mode = trim_mode
|
11
|
-
@values_file = filepath
|
12
|
-
@values_option = values
|
13
|
-
end
|
14
|
+
attr_reader :parser, :targets, :values
|
14
15
|
|
15
|
-
def
|
16
|
-
|
16
|
+
def initialize
|
17
|
+
@parser = OptionParser.instance
|
18
|
+
@targets = { $stdin => $stdout }
|
19
|
+
@values = Store.new
|
17
20
|
end
|
18
21
|
|
19
|
-
|
22
|
+
def binding
|
23
|
+
setup
|
24
|
+
super # NOTE: Kernel.#binding is private
|
25
|
+
end
|
20
26
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
require 'json'
|
25
|
-
File.open(path) { |f| JSON.parse(f.read) }
|
26
|
-
when /\A\.ya?ml\z/
|
27
|
-
require 'yaml'
|
28
|
-
# NOTE: YAML.safe_load's non-keyword arguments are deprecated in 2.6
|
29
|
-
# File.open(path) { |f| YAML.safe_load(f.read, aliases: true) }
|
30
|
-
File.open(path) { |f| YAML.safe_load(f.read, [], [], true) }
|
27
|
+
def method_missing(name, *args, &block)
|
28
|
+
if @values.respond_to?(name)
|
29
|
+
@values.public_send(name, *args, &block)
|
31
30
|
else
|
32
|
-
|
33
|
-
"Cannot load file `#{path}`. Only JSON/YAML are allowed."
|
31
|
+
super
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
35
|
+
def respond_to_missing?(name, include_private = false)
|
36
|
+
@values.respond_to?(name) || super
|
37
|
+
end
|
38
|
+
|
39
|
+
def trim_mode
|
40
|
+
@parser.trim_mode
|
40
41
|
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def setup; end
|
41
46
|
end
|
42
47
|
end
|
data/lib/ecg/option_parser.rb
CHANGED
@@ -1,115 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'optparse'
|
4
|
-
|
4
|
+
require 'singleton'
|
5
5
|
require_relative 'version'
|
6
6
|
|
7
7
|
module ECG
|
8
|
-
class OptionParser
|
9
|
-
|
10
|
-
|
11
|
-
@options = { filepath: nil, trim_mode: '<>', values: {} }
|
12
|
-
end
|
8
|
+
class OptionParser < ::OptionParser
|
9
|
+
include Singleton
|
10
|
+
attr_reader :args, :trim_mode
|
13
11
|
|
14
|
-
def
|
15
|
-
|
12
|
+
def initialize
|
13
|
+
super
|
16
14
|
|
17
|
-
|
18
|
-
@
|
19
|
-
end
|
15
|
+
@args = ARGV.dup
|
16
|
+
@trim_mode = '<>'
|
20
17
|
|
21
|
-
|
18
|
+
on_tail(
|
19
|
+
'-t', '--trim-mode MODE', %(Set ERB's trim_mode. Default is "<>".)
|
20
|
+
) do |v|
|
21
|
+
@trim_mode = v
|
22
|
+
end
|
23
|
+
on_tail('-V', '--version', 'Print version information') do
|
24
|
+
puts ver
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
on_tail('-h', '--help', 'Print this help message', &method(:print_help))
|
28
|
+
end
|
22
29
|
|
23
30
|
def banner
|
24
|
-
|
25
|
-
Usage: ecg [config.{json,yaml}] [options]
|
26
|
-
__BANNER__
|
31
|
+
'Usage: ecg [config.{json,yaml}] [options]'
|
27
32
|
end
|
28
33
|
|
29
|
-
|
30
|
-
print_help(false) if @args.length > 1
|
31
|
-
return nil if @args.empty?
|
32
|
-
|
33
|
-
path = @args.first
|
34
|
-
extname = File.extname(path)
|
35
|
-
if %w[.json .yml .yaml].none? { |ext| ext == extname }
|
36
|
-
raise ArgumentError,
|
37
|
-
"Cannot load file `#{path}`. Only JSON/YAML are allowed."
|
38
|
-
end
|
39
|
-
|
40
|
-
path
|
41
|
-
end
|
34
|
+
undef_method :parse
|
42
35
|
|
43
36
|
def parse!
|
44
37
|
print_help(false) if @args.empty?
|
45
38
|
|
46
|
-
|
47
|
-
@options[:filepath] = filepath
|
48
|
-
self
|
39
|
+
super(@args)
|
49
40
|
end
|
50
41
|
|
51
|
-
def
|
52
|
-
|
53
|
-
opt.banner = banner
|
54
|
-
opt.version = VERSION
|
55
|
-
|
56
|
-
opt.on('-v', '--values Key=Value', <<~__DESC__) do |str|
|
57
|
-
Set the key-value mapping to be embedded in the template.
|
58
|
-
__DESC__
|
59
|
-
|
60
|
-
set_option_value(*str.split('=', 2))
|
61
|
-
end
|
62
|
-
opt.on('-t', '--trim-mode MODE', <<~__DESC__) do |v|
|
63
|
-
Set ERB's trim_mode. Default is "<>".
|
64
|
-
__DESC__
|
65
|
-
@options[:trim_mode] = v
|
66
|
-
end
|
67
|
-
opt.on('-V', '--version', 'Print version information') do
|
68
|
-
puts opt.ver
|
69
|
-
exit
|
70
|
-
end
|
71
|
-
opt.on('-h', '--help', 'Print this help message', &method(:print_help))
|
72
|
-
end
|
42
|
+
def version
|
43
|
+
VERSION
|
73
44
|
end
|
74
45
|
|
46
|
+
private
|
47
|
+
|
75
48
|
def print_help(bool)
|
76
|
-
warn
|
49
|
+
Kernel.warn help
|
77
50
|
exit bool
|
78
51
|
end
|
79
|
-
|
80
|
-
def set_option_value(key, value)
|
81
|
-
keys = key.split('.').map(&:to_sym)
|
82
|
-
if keys.count == 1
|
83
|
-
set_single_option_value(keys.first, value)
|
84
|
-
else
|
85
|
-
set_nested_option_value(keys, value)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def set_single_option_value(key, value)
|
90
|
-
values = @options[:values]
|
91
|
-
if values[key].is_a? Hash
|
92
|
-
raise ArgumentError, 'Reaf keys cannot have own value.'
|
93
|
-
end
|
94
|
-
|
95
|
-
values[key] = value
|
96
|
-
values
|
97
|
-
end
|
98
|
-
|
99
|
-
def set_nested_option_value(keys, value)
|
100
|
-
values = @options[:values]
|
101
|
-
last_key = keys.pop
|
102
|
-
|
103
|
-
nested = keys.inject(values) do |result, key|
|
104
|
-
result[key] ||= {}
|
105
|
-
unless result[key].is_a? Hash
|
106
|
-
raise ArgumentError, 'Reaf keys cannot have own value.'
|
107
|
-
end
|
108
|
-
|
109
|
-
result[key]
|
110
|
-
end
|
111
|
-
nested[last_key] = value
|
112
|
-
values
|
113
|
-
end
|
114
52
|
end
|
115
53
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../option_parser'
|
4
|
+
|
5
|
+
module ECG
|
6
|
+
module Plugin
|
7
|
+
module LoadFileValues
|
8
|
+
class LoadError < ::StandardError; end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def setup
|
13
|
+
super
|
14
|
+
|
15
|
+
parser = OptionParser.instance
|
16
|
+
parser.args.inject(@values) do |result, path|
|
17
|
+
result.merge!(load_file(path))
|
18
|
+
end
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_file(path)
|
23
|
+
case File.extname(path)
|
24
|
+
when '.json'
|
25
|
+
require 'json'
|
26
|
+
File.open(path) { |f| JSON.parse(f.read) }
|
27
|
+
when /\A\.ya?ml\z/
|
28
|
+
require 'yaml'
|
29
|
+
# NOTE: YAML.safe_load's non-keyword arguments are deprecated in 2.6
|
30
|
+
# File.open(path) { |f| YAML.safe_load(f.read, aliases: true) }
|
31
|
+
File.open(path) { |f| YAML.safe_load(f.read, [], [], true) }
|
32
|
+
else
|
33
|
+
raise LoadError,
|
34
|
+
"Cannot load file `#{path}`. Only JSON/YAML are allowed."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../option_parser'
|
4
|
+
|
5
|
+
module ECG
|
6
|
+
module Plugin
|
7
|
+
module LoadOptionValues
|
8
|
+
class LoadError < ::StandardError; end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_reader :option_values
|
12
|
+
|
13
|
+
def prepended(_mod)
|
14
|
+
return if instance_variable_defined?(:@option_values)
|
15
|
+
|
16
|
+
@option_values = {}
|
17
|
+
parser = OptionParser.instance
|
18
|
+
|
19
|
+
parser.on('-v', '--values Key=Value', <<~__DESC__) do |str|
|
20
|
+
Set the key-value mapping to be embedded in the template.
|
21
|
+
__DESC__
|
22
|
+
|
23
|
+
@option_values.store(*str.split('=', 2))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def setup
|
31
|
+
super
|
32
|
+
|
33
|
+
@values.merge!(
|
34
|
+
transform_values(LoadOptionValues.option_values)
|
35
|
+
)
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def transform_values(option_values)
|
40
|
+
option_values.inject({}) do |result, (key, value)|
|
41
|
+
keys = key.split('.').map(&:to_sym)
|
42
|
+
|
43
|
+
if keys.count == 1
|
44
|
+
set_single_option_value(result, keys.first, value)
|
45
|
+
else
|
46
|
+
set_nested_option_value(result, keys, value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_single_option_value(option_values, key, value)
|
52
|
+
if option_values[key].is_a? Hash
|
53
|
+
raise LoadError, 'Reaf keys cannot have own value.'
|
54
|
+
end
|
55
|
+
|
56
|
+
option_values[key] = value
|
57
|
+
option_values
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_nested_option_value(option_values, keys, value)
|
61
|
+
last_key = keys.pop
|
62
|
+
|
63
|
+
nested = keys.inject(option_values) do |result, key|
|
64
|
+
result[key] ||= {}
|
65
|
+
unless result[key].is_a? Hash
|
66
|
+
raise LoadError, 'Reaf keys cannot have own value.'
|
67
|
+
end
|
68
|
+
|
69
|
+
result[key]
|
70
|
+
end
|
71
|
+
nested[last_key] = value
|
72
|
+
option_values
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/ecg/store.rb
CHANGED
@@ -2,45 +2,49 @@
|
|
2
2
|
|
3
3
|
module ECG
|
4
4
|
class Store
|
5
|
-
def initialize(
|
6
|
-
|
7
|
-
@store = map.to_h
|
5
|
+
def initialize(values = {})
|
6
|
+
@values = values.map { |k, v| [k.to_sym, transform_value(v)] }.to_h
|
8
7
|
end
|
9
8
|
|
10
9
|
def initialize_copy(obj)
|
11
10
|
super
|
12
|
-
@
|
13
|
-
end
|
14
|
-
|
15
|
-
def binding
|
16
|
-
super # NOTE: Kernel.#binding is private
|
11
|
+
@values = @values.dup
|
17
12
|
end
|
18
13
|
|
19
14
|
def inspect
|
20
|
-
detail = @
|
15
|
+
detail = @values.map { |k, v| " @#{k}=#{v.inspect}" }.join(',')
|
21
16
|
"#<#{self.class}:#{object_id << 1}#{detail}>"
|
22
17
|
end
|
23
18
|
|
24
|
-
def
|
25
|
-
|
19
|
+
def merge(*others)
|
20
|
+
others.inject(dup, &method(:deep_merge))
|
21
|
+
end
|
22
|
+
|
23
|
+
def merge!(*others)
|
24
|
+
others.inject(self, &method(:deep_merge))
|
25
|
+
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def method_missing(name, *args, &block)
|
28
|
+
if @values.key?(name)
|
29
|
+
@values[name]
|
30
|
+
elsif @values.respond_to?(name)
|
31
|
+
@values.public_send(name, *args, &block)
|
31
32
|
else
|
32
33
|
super
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
|
-
def respond_to_missing?(
|
37
|
-
|
37
|
+
def respond_to_missing?(name, include_private = false)
|
38
|
+
@values.key?(name) || @values.respond_to?(name) || super
|
39
|
+
end
|
38
40
|
|
39
|
-
|
41
|
+
def store(key, value)
|
42
|
+
@values.store(key.to_sym, transform_value(value))
|
40
43
|
end
|
44
|
+
alias []= store
|
41
45
|
|
42
46
|
def to_h
|
43
|
-
@
|
47
|
+
@values.transform_values(&method(:intransform_value))
|
44
48
|
end
|
45
49
|
|
46
50
|
def to_json(*args)
|
@@ -56,6 +60,20 @@ module ECG
|
|
56
60
|
|
57
61
|
private
|
58
62
|
|
63
|
+
def deep_merge(source, other)
|
64
|
+
other.keys.each do |key|
|
65
|
+
value = transform_value(other[key])
|
66
|
+
|
67
|
+
if source[key].is_a?(Store) && value.is_a?(Store)
|
68
|
+
source[key].merge!(value)
|
69
|
+
elsif !value.nil?
|
70
|
+
source[key] = value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
source
|
75
|
+
end
|
76
|
+
|
59
77
|
def intransform_value(value)
|
60
78
|
case value
|
61
79
|
when self.class
|
data/lib/ecg/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epaew
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-10-
|
11
|
+
date: 2019-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -138,6 +138,8 @@ files:
|
|
138
138
|
- lib/ecg/command.rb
|
139
139
|
- lib/ecg/context.rb
|
140
140
|
- lib/ecg/option_parser.rb
|
141
|
+
- lib/ecg/plugin/load_file_values.rb
|
142
|
+
- lib/ecg/plugin/load_option_values.rb
|
141
143
|
- lib/ecg/store.rb
|
142
144
|
- lib/ecg/version.rb
|
143
145
|
homepage: https://github.com/epaew/ecg
|
@@ -160,8 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
160
162
|
- !ruby/object:Gem::Version
|
161
163
|
version: '0'
|
162
164
|
requirements: []
|
163
|
-
|
164
|
-
rubygems_version: 2.7.6.2
|
165
|
+
rubygems_version: 3.0.3
|
165
166
|
signing_key:
|
166
167
|
specification_version: 4
|
167
168
|
summary: ecg is an ERB(eRuby) based, simple and powerful configration file generator
|