query-stream 1.4.0 → 1.6.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/CHANGELOG.md +46 -1
- data/README.md +169 -7
- data/RELEASE_NOTE.md +54 -0
- data/lib/query_stream/command.rb +2 -7
- data/lib/query_stream/configuration.rb +7 -3
- data/lib/query_stream/data_resolver.rb +2 -6
- data/lib/query_stream/errors.rb +68 -4
- data/lib/query_stream/filter_engine.rb +138 -26
- data/lib/query_stream/query_stream_parser.rb +69 -41
- data/lib/query_stream/singularize.rb +0 -2
- data/lib/query_stream/template_compiler.rb +121 -16
- data/lib/query_stream/version.rb +1 -1
- data/lib/query_stream.rb +14 -25
- data/query-stream.gemspec +7 -8
- metadata +21 -8
- data/Gemfile +0 -5
data/lib/query_stream.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# ================================================================
|
|
4
|
-
# File: lib/query_stream.rb
|
|
5
3
|
# ================================================================
|
|
6
4
|
# 責務:
|
|
7
5
|
# QueryStream gem のエントリポイント。
|
|
@@ -120,8 +118,6 @@ module QueryStream
|
|
|
120
118
|
parsed = QueryStreamParser.parse(query)
|
|
121
119
|
|
|
122
120
|
# --- Phase: Load Data ---
|
|
123
|
-
# gem 内でログ出力せず、構造化された属性を持つ例外を raise する。
|
|
124
|
-
# メッセージの構成・ログ出力・i18n は呼び出し元の責務とする。
|
|
125
121
|
data_file = DataResolver.resolve(parsed[:source], data_dir)
|
|
126
122
|
unless data_file
|
|
127
123
|
expected = File.join(data_dir, "#{parsed[:source]}.yml")
|
|
@@ -135,6 +131,7 @@ module QueryStream
|
|
|
135
131
|
records = DataResolver.load_records(data_file)
|
|
136
132
|
|
|
137
133
|
# --- Phase: Filter ---
|
|
134
|
+
FilterEngine.validate_fields!(records, parsed)
|
|
138
135
|
records = FilterEngine.apply_filters(records, parsed[:filters])
|
|
139
136
|
|
|
140
137
|
# --- Phase: Sort ---
|
|
@@ -147,13 +144,11 @@ module QueryStream
|
|
|
147
144
|
if parsed[:single_lookup]
|
|
148
145
|
case records.size
|
|
149
146
|
when 0
|
|
150
|
-
# gem 内でログ出力せず、構造化された属性を持つ警告を呼び出し元に委ねる。
|
|
151
147
|
on_warning&.call(NoResultWarning.new(query: query, location: location))
|
|
152
148
|
return ''
|
|
153
149
|
when 1
|
|
154
150
|
# 正常
|
|
155
151
|
else
|
|
156
|
-
# gem 内でログ出力せず、構造化された属性を持つ警告を呼び出し元に委ねる。
|
|
157
152
|
on_warning&.call(
|
|
158
153
|
AmbiguousQueryWarning.new(query: query, location: location, count: records.size)
|
|
159
154
|
)
|
|
@@ -168,8 +163,6 @@ module QueryStream
|
|
|
168
163
|
|
|
169
164
|
unless File.exist?(template_path)
|
|
170
165
|
hint = build_template_hint(singular, style, format, templates_dir)
|
|
171
|
-
# gem 内でログ出力せず、構造化された属性を持つ例外を raise する。
|
|
172
|
-
# メッセージの構成・ログ出力・i18n は呼び出し元の責務とする。
|
|
173
166
|
raise TemplateNotFoundError.new(
|
|
174
167
|
template_path: template_path,
|
|
175
168
|
query: query,
|
|
@@ -185,7 +178,7 @@ module QueryStream
|
|
|
185
178
|
|
|
186
179
|
# --- Phase: Post-render filter ---
|
|
187
180
|
# 展開結果を呼び出し元の後段フィルタへ通す(画像パス解決など gem 外の後処理)。
|
|
188
|
-
# コールバックが String
|
|
181
|
+
# コールバックが String 以外を返した場合は、元の展開結果を採用する。
|
|
189
182
|
return rendered unless post_render
|
|
190
183
|
|
|
191
184
|
context = {
|
|
@@ -198,6 +191,9 @@ module QueryStream
|
|
|
198
191
|
}
|
|
199
192
|
result = post_render.call(rendered, context)
|
|
200
193
|
result.is_a?(String) ? result : rendered
|
|
194
|
+
rescue InvalidQueryError => e
|
|
195
|
+
# パーサーとフィルタは記法の位置を知らないので、ここで付け足して投げ直す
|
|
196
|
+
raise e.with_context(query:, location:)
|
|
201
197
|
end
|
|
202
198
|
|
|
203
199
|
# テキスト内の QueryStream 記法を検出してリストを返す
|
|
@@ -226,16 +222,12 @@ module QueryStream
|
|
|
226
222
|
|
|
227
223
|
# その行を QueryStream 記法として扱うか。
|
|
228
224
|
#
|
|
229
|
-
#
|
|
230
|
-
# `ArgumentError
|
|
231
|
-
#
|
|
232
|
-
# ここでバイト列を書き換えて(scrub して)通す手もあるが採らない——gem が黙って
|
|
233
|
-
# 原稿のバイト列を変えると、著者が気付けない形で本文が変わる。
|
|
234
|
-
# 記法として読めない行は本文として、そのまま返すのが正しい。
|
|
225
|
+
# 不正な UTF-8 を含む行は記法として解釈せず、本文としてそのまま返す。`match?` はそういう行に
|
|
226
|
+
# `ArgumentError` を投げるので、捕まえないと展開全体が止まる。scrub しないのは、原稿のバイト列を
|
|
227
|
+
# 変えないため。
|
|
235
228
|
#
|
|
236
|
-
# EncodingError と ArgumentError
|
|
237
|
-
#
|
|
238
|
-
# strip は Encoding::CompatibilityError)、その差で展開が止まってはならない。
|
|
229
|
+
# EncodingError と ArgumentError の両方を捕まえる。壊れたバイト列に Ruby が投げる例外はメソッドで
|
|
230
|
+
# 異なる(match? / 先頭破損の lstrip は ArgumentError、strip は Encoding::CompatibilityError)。
|
|
239
231
|
def query_line?(line)
|
|
240
232
|
line.match?(QUERY_STREAM_PATTERN)
|
|
241
233
|
rescue EncodingError, ArgumentError
|
|
@@ -243,9 +235,7 @@ module QueryStream
|
|
|
243
235
|
end
|
|
244
236
|
|
|
245
237
|
# コードフェンスの開始/終了行か。
|
|
246
|
-
# `lstrip` も不正な UTF-8 で ArgumentError を投げるので、query_line?
|
|
247
|
-
# 「読めない行は本文」へ倒す。ここを素通しにすると、記法判定だけ守っても
|
|
248
|
-
# 手前のフェンス判定で落ちる。
|
|
238
|
+
# `lstrip` も不正な UTF-8 で ArgumentError を投げるので、query_line? と同じく本文として扱う。
|
|
249
239
|
def code_fence?(line)
|
|
250
240
|
line.lstrip.start_with?('```')
|
|
251
241
|
rescue EncodingError, ArgumentError
|
|
@@ -254,10 +244,9 @@ module QueryStream
|
|
|
254
244
|
|
|
255
245
|
# 引数をファイルパスとして読むべきか。
|
|
256
246
|
#
|
|
257
|
-
# `scan`
|
|
258
|
-
# `File.
|
|
259
|
-
#
|
|
260
|
-
# パスとして成立しうる形だけを先に選別し、判定自体の失敗も本文扱いに倒す。
|
|
247
|
+
# `scan` はパスと本文のどちらも受け取る。本文を `File.exist?` へ渡すと、NUL バイトを含む場合は
|
|
248
|
+
# `ArgumentError` になり、ディレクトリは `File.read` が `Errno::EISDIR` を出す。
|
|
249
|
+
# パスとして成立する形だけを先に選び、判定の失敗も本文として扱う。
|
|
261
250
|
def readable_file?(str)
|
|
262
251
|
s = str.to_s
|
|
263
252
|
return false if s.empty? || s.include?("\0") || s.include?("\n")
|
data/query-stream.gemspec
CHANGED
|
@@ -8,9 +8,10 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.authors = ['Atelier Mirai']
|
|
9
9
|
spec.email = ['contact@atelier-mirai.net']
|
|
10
10
|
|
|
11
|
-
spec.summary = '
|
|
12
|
-
spec.description = '
|
|
13
|
-
|
|
11
|
+
spec.summary = 'YAML / JSON のデータとテンプレートから、文章中の 1 行の記法を展開する Ruby ライブラリ'
|
|
12
|
+
spec.description = 'QueryStream は、文章中に書いた `= books | tags=ruby | -year | 5` のような 1 行の記法を、' \
|
|
13
|
+
'YAML / JSON のデータファイルとテンプレートを組み合わせて展開する汎用の Ruby ライブラリです。' \
|
|
14
|
+
'抽出条件、並べ替え、件数、スタイルを指定できます。'
|
|
14
15
|
spec.homepage = 'https://github.com/Atelier-Mirai/query-stream'
|
|
15
16
|
spec.license = 'MIT'
|
|
16
17
|
|
|
@@ -20,18 +21,16 @@ Gem::Specification.new do |spec|
|
|
|
20
21
|
spec.required_ruby_version = '>= 3.4'
|
|
21
22
|
|
|
22
23
|
spec.files = Dir.glob('{lib,bin}/**/*') +
|
|
23
|
-
%w[README.md CHANGELOG.md RELEASE_NOTE.md LICENSE
|
|
24
|
+
%w[README.md CHANGELOG.md RELEASE_NOTE.md LICENSE query-stream.gemspec]
|
|
24
25
|
spec.bindir = 'bin'
|
|
25
26
|
spec.executables = ['query-stream']
|
|
26
27
|
spec.require_paths = ['lib']
|
|
27
28
|
|
|
28
|
-
# Runtime dependencies
|
|
29
|
-
# logger への依存は 1.4.0 で外した。gem 自身はログを出さず、失敗は構造化例外で伝える。
|
|
30
29
|
spec.add_dependency 'samovar', '~> 2.1'
|
|
31
30
|
|
|
32
|
-
# Development dependencies
|
|
33
31
|
spec.add_development_dependency 'bundler'
|
|
34
32
|
spec.add_development_dependency 'rake', '~> 13.2'
|
|
35
|
-
spec.add_development_dependency 'minitest', '
|
|
33
|
+
spec.add_development_dependency 'minitest', '>= 5.22'
|
|
34
|
+
spec.add_development_dependency 'rubocop', '~> 1.81'
|
|
36
35
|
spec.metadata['rubygems_mfa_required'] = 'false'
|
|
37
36
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: query-stream
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Atelier Mirai
|
|
@@ -55,18 +55,32 @@ dependencies:
|
|
|
55
55
|
name: minitest
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
|
-
- - "
|
|
58
|
+
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
60
|
version: '5.22'
|
|
61
61
|
type: :development
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
|
-
- - "
|
|
65
|
+
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '5.22'
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rubocop
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '1.81'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '1.81'
|
|
82
|
+
description: QueryStream は、文章中に書いた `= books | tags=ruby | -year | 5` のような 1 行の記法を、YAML
|
|
83
|
+
/ JSON のデータファイルとテンプレートを組み合わせて展開する汎用の Ruby ライブラリです。抽出条件、並べ替え、件数、スタイルを指定できます。
|
|
70
84
|
email:
|
|
71
85
|
- contact@atelier-mirai.net
|
|
72
86
|
executables:
|
|
@@ -75,7 +89,6 @@ extensions: []
|
|
|
75
89
|
extra_rdoc_files: []
|
|
76
90
|
files:
|
|
77
91
|
- CHANGELOG.md
|
|
78
|
-
- Gemfile
|
|
79
92
|
- LICENSE
|
|
80
93
|
- README.md
|
|
81
94
|
- RELEASE_NOTE.md
|
|
@@ -112,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
112
125
|
- !ruby/object:Gem::Version
|
|
113
126
|
version: '0'
|
|
114
127
|
requirements: []
|
|
115
|
-
rubygems_version: 4.0.
|
|
128
|
+
rubygems_version: 4.0.20
|
|
116
129
|
specification_version: 4
|
|
117
|
-
summary:
|
|
130
|
+
summary: YAML / JSON のデータとテンプレートから、文章中の 1 行の記法を展開する Ruby ライブラリ
|
|
118
131
|
test_files: []
|