yayaml 0.1.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 +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +38 -0
- data/LICENSE.txt +21 -0
- data/README.md +23 -0
- data/Rakefile +8 -0
- data/args.rb +8 -0
- data/discourse.yml +5557 -0
- data/en.yml +22 -0
- data/example.rb +89 -0
- data/example.yml +17 -0
- data/exe/ya +167 -0
- data/lib/yayaml/handler.rb +88 -0
- data/lib/yayaml/matcher.rb +52 -0
- data/lib/yayaml/version.rb +5 -0
- data/lib/yayaml.rb +5 -0
- data/sig/yay/rb.rbs +6 -0
- data/test.yml +9 -0
- metadata +81 -0
data/en.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
en:
|
2
|
+
hello: "Hello world"
|
3
|
+
timeline:
|
4
|
+
events:
|
5
|
+
invoice:
|
6
|
+
payment_failed: "Invoice payment failed"
|
7
|
+
outstanding_invoices_paid: Outstanding invoices paid
|
8
|
+
source_added: Customer added default credit card
|
9
|
+
source_updated: Customer updated default credit card
|
10
|
+
subscription:
|
11
|
+
canceled: "Subscription canceled"
|
12
|
+
canceled_at_period_end: "Subscription set to cancel at the end of the current billing cycle"
|
13
|
+
subscription_schedule_released: "Subscription schedule released"
|
14
|
+
updated_through_creation: "Subscription was updated via immediate downgrade in Stripe"
|
15
|
+
testing:
|
16
|
+
event_for_test_without_interpolation: "testing"
|
17
|
+
event_for_test_with_interpolation: "testing %{test_var}"
|
18
|
+
errors:
|
19
|
+
business_address:
|
20
|
+
argument_error:
|
21
|
+
already_exists: "A business address is already associated with this account"
|
22
|
+
incorrect_zip_format: "should be in the format of 12345 or 12345-6789"
|
data/example.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "psych"
|
4
|
+
require "pry"
|
5
|
+
|
6
|
+
# Handler for detecting scalar values
|
7
|
+
class YayHandler < Psych::Handler
|
8
|
+
# States:
|
9
|
+
# a -> start_map -> b
|
10
|
+
# b -> scalar -> c (push key)
|
11
|
+
# b -> end_map -> b (pop key)
|
12
|
+
# c -> scalar -> b (push value, pop key)
|
13
|
+
# c -> start_map -> b (push map)
|
14
|
+
# c -> end_map -> b (pop key)
|
15
|
+
|
16
|
+
attr_accessor :parser
|
17
|
+
|
18
|
+
def initialize(*args)
|
19
|
+
@state = :a
|
20
|
+
@prev = nil
|
21
|
+
@keys = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_mapping(*args)
|
25
|
+
@prev = @state
|
26
|
+
case @state
|
27
|
+
when :a
|
28
|
+
@state = :b
|
29
|
+
when :c
|
30
|
+
@state = :b
|
31
|
+
else
|
32
|
+
fail
|
33
|
+
end
|
34
|
+
|
35
|
+
transition("start map")
|
36
|
+
end
|
37
|
+
|
38
|
+
def end_mapping
|
39
|
+
@prev = @state
|
40
|
+
case @state
|
41
|
+
when :b, :c
|
42
|
+
@state = :b
|
43
|
+
@keys.pop
|
44
|
+
else
|
45
|
+
fail
|
46
|
+
end
|
47
|
+
|
48
|
+
transition("end map")
|
49
|
+
end
|
50
|
+
|
51
|
+
def scalar(value, *args)
|
52
|
+
@prev = @state
|
53
|
+
case @state
|
54
|
+
when :b
|
55
|
+
@state = :c
|
56
|
+
@keys << value
|
57
|
+
when :c
|
58
|
+
@state = :b
|
59
|
+
puts "#{@keys.join('.')}:#{parser.mark.line + 1}: #{value}"
|
60
|
+
@keys.pop
|
61
|
+
else
|
62
|
+
fail
|
63
|
+
end
|
64
|
+
|
65
|
+
transition("scalar")
|
66
|
+
end
|
67
|
+
|
68
|
+
def transition(event)
|
69
|
+
# puts "#@prev -> (#{event}) -> #@state"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# yaml = File.read("/Users/timuruski/en.yml")
|
74
|
+
yaml = DATA
|
75
|
+
handler = YayHandler.new
|
76
|
+
parser = Psych::Parser.new(handler)
|
77
|
+
handler.parser = parser
|
78
|
+
parser.parse(yaml)
|
79
|
+
|
80
|
+
__END__
|
81
|
+
---
|
82
|
+
en:
|
83
|
+
page1:
|
84
|
+
button: Hello
|
85
|
+
page2:
|
86
|
+
button: Goodbye
|
87
|
+
fr:
|
88
|
+
page1:
|
89
|
+
button: Bonjour
|
data/example.yml
ADDED
data/exe/ya
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "psych"
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
at_exit do
|
7
|
+
Yay::Command.new.run
|
8
|
+
end
|
9
|
+
|
10
|
+
module Yay
|
11
|
+
BOLD_RED = "\e[1m\e[38;5;1m"
|
12
|
+
RESET = "\e[0m"
|
13
|
+
|
14
|
+
class Command
|
15
|
+
def initialize(args = ARGV)
|
16
|
+
parser = OptionParser.new do |parser|
|
17
|
+
parser.on("-d", "--debug") { $DEBUG = true }
|
18
|
+
parser.on("-i", "--ignore-case") { |value| @ignore_case = value }
|
19
|
+
parser.on("-p", "--search-path") { |value| @match_path = value }
|
20
|
+
end
|
21
|
+
|
22
|
+
parser.banner = "Usage yay [options] \"<search pattern>\" <inputs>"
|
23
|
+
if args.empty?
|
24
|
+
abort parser.help
|
25
|
+
else
|
26
|
+
parser.parse!
|
27
|
+
end
|
28
|
+
|
29
|
+
@search_pattern = args.shift
|
30
|
+
@paths = args.flat_map { |filename| File.directory?(filename) ? Dir.glob("#{filename}/**/*.yml") : filename }
|
31
|
+
@paths.delete("--")
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: Use abort if all YAML paths fail to parse.
|
35
|
+
def run
|
36
|
+
if @paths.empty?
|
37
|
+
warn "Reading from STDIN..." if STDIN.tty?
|
38
|
+
parse_yaml(STDIN, "STDIN")
|
39
|
+
else
|
40
|
+
@paths.each do |path|
|
41
|
+
File.open(path) do |file|
|
42
|
+
parse_yaml(file, file.path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private def parse_yaml(input, filename)
|
49
|
+
handler = YayHandler.new(@search_pattern, @match_path, @ignore_case, filename)
|
50
|
+
Psych::Parser.new(handler).parse(input)
|
51
|
+
rescue Psych::SyntaxError => e
|
52
|
+
warn "Failed to parse YAML #{e.file}:#{e.line}:#{e.offset}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# See these for details:
|
57
|
+
# https://ruby-doc.org/3.2.2/exts/psych/Psych/Handler.html
|
58
|
+
# https://github.com/ruby/psych/blob/master/lib/psych/handler.rb
|
59
|
+
class YayHandler < Psych::Handler
|
60
|
+
# State transitions:
|
61
|
+
# branch -> start_map -> branch (no-op)
|
62
|
+
# leaf -> start_map -> branch (no-op)
|
63
|
+
# branch -> scalar -> leaf (push key)
|
64
|
+
# leaf -> scalar -> leaf (pop key, handle value)
|
65
|
+
# branch -> end_map -> branch (pop key)
|
66
|
+
# leaf -> end_map -> branch (pop key)
|
67
|
+
|
68
|
+
def initialize(search_pattern, match_path, ignore_case, filename, *args)
|
69
|
+
@search_pattern = Regexp.new(search_pattern, ignore_case ? Regexp::IGNORECASE : nil)
|
70
|
+
@ignore_case = ignore_case
|
71
|
+
@match_path = match_path
|
72
|
+
@filename = filename
|
73
|
+
end
|
74
|
+
|
75
|
+
def start_document(*args)
|
76
|
+
debug "--> Start document #{@filename}"
|
77
|
+
|
78
|
+
@state = :branch
|
79
|
+
@prev = nil
|
80
|
+
@keys = []
|
81
|
+
@line = 0
|
82
|
+
@col = 0
|
83
|
+
end
|
84
|
+
|
85
|
+
def start_mapping(*args)
|
86
|
+
transition("start map") do
|
87
|
+
case @state
|
88
|
+
when :branch, :leaf
|
89
|
+
@state = :branch
|
90
|
+
else
|
91
|
+
panic("start map")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def end_mapping
|
97
|
+
transition("end map") do
|
98
|
+
case @state
|
99
|
+
when :branch, :leaf
|
100
|
+
@state = :branch
|
101
|
+
@keys.pop
|
102
|
+
else
|
103
|
+
panic("end map")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def scalar(value, *args)
|
109
|
+
transition("scalar") do
|
110
|
+
case @state
|
111
|
+
when :branch
|
112
|
+
@state = :leaf
|
113
|
+
@keys.push(value)
|
114
|
+
when :leaf
|
115
|
+
@state = :branch
|
116
|
+
handle_leaf(value)
|
117
|
+
@keys.pop
|
118
|
+
else
|
119
|
+
panic("scalar")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def event_location(start_line, start_column, _end_line, _end_column)
|
125
|
+
@line = start_line + 1
|
126
|
+
@col = start_column + 1
|
127
|
+
end
|
128
|
+
|
129
|
+
private def handle_leaf(value)
|
130
|
+
path = @keys.join(".")
|
131
|
+
|
132
|
+
if @match_path
|
133
|
+
if match = path.match(@search_pattern)
|
134
|
+
if $stdout.tty?
|
135
|
+
path_matched = path.sub(@search_pattern, "#{BOLD_RED}#{match}#{RESET}")
|
136
|
+
puts "#{@filename}:#{@line} #{path_matched}: #{value}"
|
137
|
+
else
|
138
|
+
puts "#{@filename}:#{@line} #{path}: #{value}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
else
|
142
|
+
if match = value.match(@search_pattern)
|
143
|
+
if $stdout.tty?
|
144
|
+
value_matched = value.sub(@search_pattern, "#{BOLD_RED}#{match}#{RESET}")
|
145
|
+
puts "#{@filename}:#{@line} #{path}: #{value_matched}"
|
146
|
+
else
|
147
|
+
puts "#{@filename}:#{@line} #{path}: #{value}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
private def transition(event)
|
154
|
+
@prev = @state
|
155
|
+
yield
|
156
|
+
debug "#@prev -> (#{event}) -> #@state"
|
157
|
+
end
|
158
|
+
|
159
|
+
private def debug(msg)
|
160
|
+
warn msg if $DEBUG || ENV["DEBUG"]
|
161
|
+
end
|
162
|
+
|
163
|
+
private def panic(event)
|
164
|
+
fail "Failed to parse #{event} at #{@filename}:#{@line}:#{@col}, state #{@state}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "psych"
|
2
|
+
|
3
|
+
module Yayaml
|
4
|
+
class Handler < Psych::Handler
|
5
|
+
# State transitions:
|
6
|
+
# branch -> start_map -> branch (no-op)
|
7
|
+
# leaf -> start_map -> branch (no-op)
|
8
|
+
# branch -> scalar -> leaf (push key)
|
9
|
+
# leaf -> scalar -> leaf (pop key, handle value)
|
10
|
+
# branch -> end_map -> branch (pop key)
|
11
|
+
# leaf -> end_map -> branch (pop key)
|
12
|
+
|
13
|
+
attr_reader :matcher, :filename
|
14
|
+
|
15
|
+
def initialize(matcher, filename: nil)
|
16
|
+
@matcher = matcher
|
17
|
+
@filename = filename
|
18
|
+
end
|
19
|
+
|
20
|
+
def start_document(*args)
|
21
|
+
debug "--> Start document #{filename}"
|
22
|
+
|
23
|
+
@state = :branch
|
24
|
+
@prev = nil
|
25
|
+
@keys = []
|
26
|
+
@line = 0
|
27
|
+
@col = 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def start_mapping(*args)
|
31
|
+
transition("start map") do
|
32
|
+
case @state
|
33
|
+
when :branch, :leaf
|
34
|
+
@state = :branch
|
35
|
+
else
|
36
|
+
panic("start_mapping")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def end_mapping
|
42
|
+
transition("end map") do
|
43
|
+
case @state
|
44
|
+
when :branch, :leaf
|
45
|
+
@state = :branch
|
46
|
+
@keys.pop
|
47
|
+
else
|
48
|
+
panic("end_mapping")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def scalar(value, *args)
|
54
|
+
transition("scalar") do
|
55
|
+
case @state
|
56
|
+
when :branch
|
57
|
+
@state = :leaf
|
58
|
+
@keys.push(value)
|
59
|
+
when :leaf
|
60
|
+
@state = :branch
|
61
|
+
matcher.on_node(@keys.dup, value)
|
62
|
+
@keys.pop
|
63
|
+
else
|
64
|
+
panic("scalar")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def event_location(start_line, start_column, end_line, end_column)
|
70
|
+
@line = start_line + 1
|
71
|
+
@col = start_column + 1
|
72
|
+
end
|
73
|
+
|
74
|
+
private def transition(event)
|
75
|
+
@prev = @state
|
76
|
+
yield
|
77
|
+
# debug "#@prev -> (#{event}) -> #@state"
|
78
|
+
end
|
79
|
+
|
80
|
+
private def debug(msg)
|
81
|
+
warn msg if @debug
|
82
|
+
end
|
83
|
+
|
84
|
+
private def panic(event)
|
85
|
+
fail "Failed to parse #{event} at #{filename}:#{@line}:#{@col}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Yayaml
|
2
|
+
class Matcher
|
3
|
+
BOLD_RED = "\e[1m\e[38;5;1m"
|
4
|
+
RESET = "\e[0m"
|
5
|
+
|
6
|
+
attr_reader :nodes
|
7
|
+
|
8
|
+
def initialize(pattern, match_path: false, ignore_case: false)
|
9
|
+
@pattern = Regexp.compile(pattern)
|
10
|
+
@match_path = match_path
|
11
|
+
@ignore_case = ignore_case
|
12
|
+
|
13
|
+
@nodes = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_node(keys, value)
|
17
|
+
@nodes << [keys, value]
|
18
|
+
|
19
|
+
path = keys.join(".")
|
20
|
+
|
21
|
+
if @match_path
|
22
|
+
if (match = path.match(@pattern))
|
23
|
+
path_matched = path.sub(@pattern, "#{BOLD_RED}#{match}#{RESET}")
|
24
|
+
# puts "#{path_matched}:#{@line}: #{value}"
|
25
|
+
puts "#{@filename}:#{@line} #{path_matched}: #{value}"
|
26
|
+
end
|
27
|
+
else
|
28
|
+
if (match = value.match(@pattern))
|
29
|
+
value_matched = value.sub(@pattern, "#{BOLD_RED}#{match}#{RESET}")
|
30
|
+
# puts "#{path}:#{@line}: #{value_matched}"
|
31
|
+
puts "#{@filename}:#{@line} #{path}: #{value_matched}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private def match_value(keys, value)
|
37
|
+
if (match = value.match(@pattern))
|
38
|
+
value_matched = value.sub(@pattern, "#{BOLD_RED}#{match}#{RESET}")
|
39
|
+
# puts "#{path}:#{@line}: #{value_matched}"
|
40
|
+
puts "#{@filename}:#{@line} #{path}: #{value_matched}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private def match_path(keys, value)
|
45
|
+
if (match = path.match(@pattern))
|
46
|
+
path_matched = path.sub(@pattern, "#{BOLD_RED}#{match}#{RESET}")
|
47
|
+
# puts "#{path_matched}:#{@line}: #{value}"
|
48
|
+
puts "#{@filename}:#{@line} #{path_matched}: #{value}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/yayaml.rb
ADDED
data/sig/yay/rb.rbs
ADDED
data/test.yml
ADDED
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yayaml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tim Uruski
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-04-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: psych
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.5
|
27
|
+
description: A tool for searching the structure of YAML like i18n files
|
28
|
+
email:
|
29
|
+
- tim@uruski.xyz
|
30
|
+
executables:
|
31
|
+
- ya
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".rspec"
|
36
|
+
- CHANGELOG.md
|
37
|
+
- CODE_OF_CONDUCT.md
|
38
|
+
- Gemfile
|
39
|
+
- Gemfile.lock
|
40
|
+
- LICENSE.txt
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- args.rb
|
44
|
+
- discourse.yml
|
45
|
+
- en.yml
|
46
|
+
- example.rb
|
47
|
+
- example.yml
|
48
|
+
- exe/ya
|
49
|
+
- lib/yayaml.rb
|
50
|
+
- lib/yayaml/handler.rb
|
51
|
+
- lib/yayaml/matcher.rb
|
52
|
+
- lib/yayaml/version.rb
|
53
|
+
- sig/yay/rb.rbs
|
54
|
+
- test.yml
|
55
|
+
homepage: https://github.com/timuruski/yayaml
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata:
|
59
|
+
homepage_uri: https://github.com/timuruski/yayaml
|
60
|
+
source_code_uri: https://github.com/timuruski/yayaml
|
61
|
+
changelog_uri: https://github.com/timuruski/yayaml/tree/master/CHANGELOG.md
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 2.6.0
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubygems_version: 3.5.5
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: A tool for searching YAML files
|
81
|
+
test_files: []
|