Module: XeroKiwi::Query::Filter
- Defined in:
- lib/xero_kiwi/query/filter.rb
Overview
Compiles a Ruby-native filter into Xero’s ‘where` expression syntax.
XeroKiwi::Query::Filter.compile(
{ status: "AUTHORISED", date: Date.new(2026, 1, 1)..Date.new(2026, 4, 1) },
fields: Invoice.query_fields
)
# => 'Status=="AUTHORISED"&&Date>=DateTime(2026,1,1)&&Date<=DateTime(2026,4,1)'
Supported input shapes:
nil → nil
String → passthrough (raw escape hatch)
Hash → walked; each pair becomes `Path==Literal`
Array value → IN-semantics `(Path==x || Path==y)`
Range value → `Path>=lo && Path<=hi`
Hash value → nested `Parent.Child==Literal`, using the child's
query_fields schema
Unknown field keys raise ArgumentError so typos surface immediately.
Class Method Summary collapse
- .compile(where, fields:) ⇒ Object
- .compile_hash(hash, fields, prefix) ⇒ Object
-
.compile_pair(key, value, fields, prefix) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity.
-
.date_literal(value) ⇒ Object
Date → render as-is (a Date IS a day, no timezone shifting).
- .literal(value, type) ⇒ Object
- .quote_string(value) ⇒ Object
Class Method Details
.compile(where, fields:) ⇒ Object
30 31 32 33 34 35 36 37 |
# File 'lib/xero_kiwi/query/filter.rb', line 30 def compile(where, fields:) return nil if where.nil? return where if where.is_a?(String) raise ArgumentError, "where must be a Hash or String, got #{where.class}" unless where.is_a?(Hash) compile_hash(where, fields, nil) end |
.compile_hash(hash, fields, prefix) ⇒ Object
39 40 41 |
# File 'lib/xero_kiwi/query/filter.rb', line 39 def compile_hash(hash, fields, prefix) hash.map { |key, value| compile_pair(key, value, fields, prefix) }.join("&&") end |
.compile_pair(key, value, fields, prefix) ⇒ Object
rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/xero_kiwi/query/filter.rb', line 43 def compile_pair(key, value, fields, prefix) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity spec = fields.fetch(key) do raise ArgumentError, "unknown filter field: #{key.inspect}" end path = prefix ? "#{prefix}.#{spec[:path]}" : spec[:path] case value when Array "(#{value.map { |v| "#{path}==#{literal(v, spec[:type])}" }.join("||")})" when Range "#{path}>=#{literal(value.begin, spec[:type])}&&#{path}<=#{literal(value.end, spec[:type])}" when Hash raise ArgumentError, "#{key.inspect} is not a nested filter field" unless spec[:type] == :nested compile_hash(value, spec[:fields], path) else "#{path}==#{literal(value, spec[:type])}" end end |
.date_literal(value) ⇒ Object
Date → render as-is (a Date IS a day, no timezone shifting). Time → convert to UTC before extracting Y/M/D so ‘Time.new(…, “+10:00”)`
doesn't render the local-timezone day.
83 84 85 86 87 88 89 90 |
# File 'lib/xero_kiwi/query/filter.rb', line 83 def date_literal(value) case value when Date then "DateTime(#{value.year},#{value.month},#{value.day})" when Time then t = value.utc "DateTime(#{t.year},#{t.month},#{t.day})" else raise ArgumentError, "cannot render #{value.class} as :date" end end |
.literal(value, type) ⇒ Object
64 65 66 67 68 69 70 71 72 73 |
# File 'lib/xero_kiwi/query/filter.rb', line 64 def literal(value, type) case type when :guid then %(Guid("#{value}")) when :string, :enum then quote_string(value) when :date then date_literal(value) when :bool then value ? "true" : "false" when :decimal then value.to_s else raise ArgumentError, "cannot render #{value.inspect} as #{type.inspect}" end end |
.quote_string(value) ⇒ Object
75 76 77 78 |
# File 'lib/xero_kiwi/query/filter.rb', line 75 def quote_string(value) escaped = value.to_s.gsub("\\", "\\\\\\\\").gsub('"', '\\"') %("#{escaped}") end |