Module: SmilyCli::QueryOptions
- Defined in:
- lib/smily_cli/query_options.rb
Overview
Turns user-facing flags (--filter status=booked, --query per_page=100,
--fields id,name, --include availability) into the flat query hash the
API v3 expects. v3 filtering is plain query parameters, so --filter and
--query share one mechanism; --filter simply reads as intent.
Constant Summary collapse
- OPERATORS =
Comparison operators the API v3 filter DSL accepts (mirrors the operators reported per attribute by the
resource_schema/api_v3_resource_schematool). Used to recognize theattribute[op]=valuefilter syntax. %w[eq not_eq gt gteq lt lteq in matches does_not_match].freeze
Class Method Summary collapse
- .as_operator(condition) ⇒ Object private
-
.build(fields: nil, include: nil, filter: [], query: []) ⇒ Hash{String=>Object}
Assemble the final query hash for a request from the standard flags.
-
.coerce_filter_value(operator, value) ⇒ Object
private
intakes a comma-separated set; every other operator a single value. -
.coerce_scalar(value) ⇒ Object
private
Coerce a raw string into the natural JSON scalar so numeric/boolean attributes compare as themselves; anything else (incl. ISO 8601) stays a string.
-
.condition_for(operator, value) ⇒ Object
private
A single filter condition: an
{ op, value }object when an operator was given, else the bare coerced equality value. -
.merge_condition(filter, attribute, condition) ⇒ Object
private
Add one condition to the filter, arraying (AND) when the attribute already has one.
- .normalize_list(value) ⇒ Object private
-
.parse_filter(pairs) ⇒ Hash{String=>Object}
Parse
key=value/key[op]=valuefilter pairs into the structured filter object the API v3 list tool accepts (via MCPapi_v3_list):. -
.parse_operator(key) ⇒ Object
private
Split an
attribute[op]key into[attribute, op], validating the operator; a plainattributereturns[attribute, nil](equality). -
.parse_pairs(pairs) ⇒ Hash{String=>Object}
Parse
key=valuestrings into a hash. - .split_pair(pair) ⇒ Object private
Class Method Details
.as_operator(condition) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
130 131 132 |
# File 'lib/smily_cli/query_options.rb', line 130 def as_operator(condition) condition.is_a?(Hash) ? condition : { "op" => "eq", "value" => condition } end |
.build(fields: nil, include: nil, filter: [], query: []) ⇒ Hash{String=>Object}
Assemble the final query hash for a request from the standard flags.
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/smily_cli/query_options.rb', line 71 def build(fields: nil, include: nil, filter: [], query: []) result = {} result.merge!(parse_pairs(query)) result.merge!(parse_pairs(filter)) if (inc = normalize_list(include)) result["include"] = inc.join(",") end result["fields"] = fields.join(",") if fields && !fields.empty? result end |
.coerce_filter_value(operator, value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
in takes a comma-separated set; every other operator a single value.
136 137 138 139 140 |
# File 'lib/smily_cli/query_options.rb', line 136 def coerce_filter_value(operator, value) return value.split(",").map { |v| coerce_scalar(v.strip) } if operator == "in" coerce_scalar(value) end |
.coerce_scalar(value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Coerce a raw string into the natural JSON scalar so numeric/boolean attributes compare as themselves; anything else (incl. ISO 8601) stays a string.
146 147 148 149 150 151 152 153 154 |
# File 'lib/smily_cli/query_options.rb', line 146 def coerce_scalar(value) case value when /\A-?\d+\z/ then value.to_i when /\A-?\d+\.\d+\z/ then Float(value) when "true" then true when "false" then false else value end end |
.condition_for(operator, value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
A single filter condition: an { op, value } object when an operator was
given, else the bare coerced equality value.
94 95 96 97 98 |
# File 'lib/smily_cli/query_options.rb', line 94 def condition_for(operator, value) return coerce_scalar(value) unless operator { "op" => operator, "value" => coerce_filter_value(operator, value) } end |
.merge_condition(filter, attribute, condition) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Add one condition to the filter, arraying (AND) when the attribute already
has one. A bare equality value is promoted to an { op: "eq" } object when
it must share an array with operator conditions, keeping the array uniform.
119 120 121 122 123 124 125 126 127 |
# File 'lib/smily_cli/query_options.rb', line 119 def merge_condition(filter, attribute, condition) if filter.key?(attribute) existing = filter[attribute] list = existing.is_a?(Array) ? existing : [as_operator(existing)] filter[attribute] = list << as_operator(condition) else filter[attribute] = condition end end |
.normalize_list(value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
157 158 159 160 161 162 |
# File 'lib/smily_cli/query_options.rb', line 157 def normalize_list(value) return nil if value.nil? list = Array(value).flat_map { |v| v.to_s.split(",") }.map(&:strip).reject(&:empty?) list.empty? ? nil : list end |
.parse_filter(pairs) ⇒ Hash{String=>Object}
Parse key=value / key[op]=value filter pairs into the structured
filter object the API v3 list tool accepts (via MCP api_v3_list):
* a bare (coerced) value for plain equality,
* an `{ "op", "value" }` object for a single operator, and
* an array of such objects when one attribute carries more than one
condition (AND) — e.g. a numeric or date range.
Examples:
["currency=EUR"] => { "currency" => "EUR" }
["adults[gteq]=2"] => { "adults" => { "op" => "gteq", "value" => 2 } }
["final_price[gteq]=600",
"final_price[lt]=800"] => { "final_price" =>
[ { "op" => "gteq", "value" => 600 },
{ "op" => "lt", "value" => 800 } ] }
["reference[in]=A,B"] => { "reference" => { "op" => "in", "value" => %w[A B] } }
Values are coerced (integer/float/boolean) so numeric attributes compare as numbers rather than strings; ISO 8601 timestamps stay strings.
56 57 58 59 60 61 62 |
# File 'lib/smily_cli/query_options.rb', line 56 def parse_filter(pairs) Array(pairs).each_with_object({}) do |pair, acc| key, value = split_pair(pair) attribute, operator = parse_operator(key) merge_condition(acc, attribute, condition_for(operator, value)) end end |
.parse_operator(key) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Split an attribute[op] key into [attribute, op], validating the
operator; a plain attribute returns [attribute, nil] (equality).
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/smily_cli/query_options.rb', line 103 def parse_operator(key) match = key.match(/\A(?<attribute>.+?)\[(?<op>[a-z_]+)\]\z/) return [key, nil] unless match operator = match[:op] unless OPERATORS.include?(operator) raise UsageError, "Unknown filter operator #{operator.inspect} in #{key.inspect}. Valid: #{OPERATORS.join(', ')}." end [match[:attribute], operator] end |
.parse_pairs(pairs) ⇒ Hash{String=>Object}
Parse key=value strings into a hash. A repeated key accumulates into an
array (so --filter status=booked --filter status=tentative sends both).
22 23 24 25 26 27 28 29 30 31 |
# File 'lib/smily_cli/query_options.rb', line 22 def parse_pairs(pairs) Array(pairs).each_with_object({}) do |pair, acc| key, value = split_pair(pair) acc[key] = if acc.key?(key) Array(acc[key]) << value else value end end end |
.split_pair(pair) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
83 84 85 86 87 88 89 |
# File 'lib/smily_cli/query_options.rb', line 83 def split_pair(pair) str = pair.to_s key, sep, value = str.partition("=") raise UsageError, "Invalid key=value pair #{pair.inspect} (expected e.g. status=booked)" if sep.empty? [key.strip, value] end |