Module: QueryStream
- Defined in:
- lib/query_stream/errors.rb,
lib/query_stream.rb,
lib/query_stream/command.rb,
lib/query_stream/version.rb,
lib/query_stream/singularize.rb,
lib/query_stream/configuration.rb,
lib/query_stream/data_resolver.rb,
lib/query_stream/filter_engine.rb,
lib/query_stream/template_compiler.rb,
lib/query_stream/query_stream_parser.rb
Overview
File: lib/query_stream/query_stream_parser.rb
責務:
QueryStream 記法(= books | =ruby | -title | 5 | :full)を
パースし、構造化されたクエリハッシュを返す。
パイプライン:
1. Source - データ名(必須)
2. Filter - 抽出条件(field=value, 比較演算子, 範囲指定)
3. Sort - ソート条件(-field / +field)
4. Limit - 件数制限(正の整数)
5. Style - スタイル名(:stylename または :stylename.ext)
トークンの自動判別:
各トークンは形式で一意に判別される(省略・順序入れ替えに対応)
Defined Under Namespace
Modules: Command, DataResolver, FilterEngine, QueryStreamParser, Singularize, TemplateCompiler Classes: AmbiguousQueryWarning, Configuration, DataNotFoundError, Error, InvalidDateError, NoResultWarning, TemplateNotFoundError, UnknownKeyError, Warning
Constant Summary collapse
- QUERY_STREAM_PATTERN =
QueryStream 記法を検出する正規表現行頭 = の直後に英数字/ハイフン/アンダースコアのデータ名(スペースは任意)
/^=\s*([a-zA-Z][a-zA-Z0-9_-]*)(?:\s*\|.*)?$/- VERSION =
'1.2.0'
Class Method Summary collapse
-
.configuration ⇒ Configuration
グローバル設定を返す.
-
.configure {|Configuration| ... } ⇒ Object
設定をブロックで変更する.
-
.logger ⇒ Logger
ロガーへのショートカット.
-
.render(content, source_filename: nil, data_dir: nil, templates_dir: nil, on_error: nil, on_warning: nil) ⇒ String
テキストコンテンツ内の QueryStream 記法をすべて展開する 1行の展開に失敗しても残りの行の処理を継続する。 エラー情報は例外の属性として呼び出し元に委ねる(gem 内ではログ出力しない)。.
-
.render_query(query, line_number: nil, source_filename: nil, data_dir: nil, templates_dir: nil, on_warning: nil) ⇒ String
単一の QueryStream 記法を展開する.
-
.scan(path_or_content) ⇒ Array<String>
テキスト内の QueryStream 記法を検出してリストを返す.
Class Method Details
.configuration ⇒ Configuration
グローバル設定を返す
35 36 37 |
# File 'lib/query_stream.rb', line 35 def configuration @configuration ||= Configuration.new end |
.configure {|Configuration| ... } ⇒ Object
設定をブロックで変更する
41 42 43 |
# File 'lib/query_stream.rb', line 41 def configure yield(configuration) end |
.logger ⇒ Logger
ロガーへのショートカット
47 48 49 |
# File 'lib/query_stream.rb', line 47 def logger configuration.logger end |
.render(content, source_filename: nil, data_dir: nil, templates_dir: nil, on_error: nil, on_warning: nil) ⇒ String
テキストコンテンツ内の QueryStream 記法をすべて展開する1行の展開に失敗しても残りの行の処理を継続する。エラー情報は例外の属性として呼び出し元に委ねる(gem 内ではログ出力しない)。
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/query_stream.rb', line 61 def render(content, source_filename: nil, data_dir: nil, templates_dir: nil, on_error: nil, on_warning: nil) data_dir ||= configuration.data_dir templates_dir ||= configuration.templates_dir lines = content.lines result = [] in_code_block = false lines.each_with_index do |line, idx| line_number = idx + 1 # コードブロック内はスキップ if line.lstrip.start_with?('```') in_code_block = !in_code_block result << line next end if in_code_block result << line next end # QueryStream 記法の検出 if line.match?(QUERY_STREAM_PATTERN) begin = render_query( line.chomp, line_number:, source_filename:, data_dir:, templates_dir:, on_warning: ) result << << "\n" rescue Error => e # 1行の失敗で残りの展開を止めない。エラー処理は呼び出し元に委ねる。 on_error&.call(e) result << line end else result << line end end result.join end |
.render_query(query, line_number: nil, source_filename: nil, data_dir: nil, templates_dir: nil, on_warning: nil) ⇒ String
単一の QueryStream 記法を展開する
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/query_stream.rb', line 111 def render_query(query, line_number: nil, source_filename: nil, data_dir: nil, templates_dir: nil, on_warning: nil) data_dir ||= configuration.data_dir templates_dir ||= configuration.templates_dir location = source_filename ? "#{source_filename}:#{line_number}" : "行#{line_number}" # --- Phase: Parse --- parsed = QueryStreamParser.parse(query) # --- Phase: Load Data --- # gem 内でログ出力せず、構造化された属性を持つ例外を raise する。 # メッセージの構成・ログ出力・i18n は呼び出し元の責務とする。 data_file = DataResolver.resolve(parsed[:source], data_dir) unless data_file expected = File.join(data_dir, "#{parsed[:source]}.yml") raise DataNotFoundError.new( expected_path: expected, query: query, location: location ) end records = DataResolver.load_records(data_file) # --- Phase: Filter --- records = FilterEngine.apply_filters(records, parsed[:filters]) # --- Phase: Sort --- records = FilterEngine.apply_sort(records, parsed[:sort]) if parsed[:sort] # --- Phase: Limit --- records = records.first(parsed[:limit]) if parsed[:limit] # --- Phase: Single record warning --- if parsed[:single_lookup] case records.size when 0 # gem 内でログ出力せず、構造化された属性を持つ警告を呼び出し元に委ねる。 on_warning&.call(NoResultWarning.new(query: query, location: location)) return '' when 1 # 正常 else # gem 内でログ出力せず、構造化された属性を持つ警告を呼び出し元に委ねる。 on_warning&.call( AmbiguousQueryWarning.new(query: query, location: location, count: records.size) ) end end # --- Phase: Resolve Template --- singular = Singularize.call(parsed[:source]) style = parsed[:style] format = parsed[:format] template_path = resolve_template_path(singular, style, format, templates_dir) unless File.exist?(template_path) hint = build_template_hint(singular, style, format, templates_dir) # gem 内でログ出力せず、構造化された属性を持つ例外を raise する。 # メッセージの構成・ログ出力・i18n は呼び出し元の責務とする。 raise TemplateNotFoundError.new( template_path: template_path, query: query, location: location, hint: hint ) end template_content = File.read(template_path, encoding: 'utf-8') # --- Phase: Render --- TemplateCompiler.render(template_content, records, source_filename:, line_number:) end |
.scan(path_or_content) ⇒ Array<String>
テキスト内の QueryStream 記法を検出してリストを返す
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/query_stream.rb', line 187 def scan(path_or_content) content = File.exist?(path_or_content) ? File.read(path_or_content, encoding: 'utf-8') : path_or_content lines = content.lines queries = [] in_code_block = false lines.each do |line| if line.lstrip.start_with?('```') in_code_block = !in_code_block next end next if in_code_block queries << line.chomp if line.match?(QUERY_STREAM_PATTERN) end queries end |