Class: Glancer::Workflow::ARExecutor
- Inherits:
-
Object
- Object
- Glancer::Workflow::ARExecutor
- Defined in:
- lib/glancer/workflow/ar_executor.rb
Class Method Summary collapse
-
.drop_all_nil_columns(rows) ⇒ Object
Removes columns where every row has a nil value (e.g. ‘id` when using .select(“col1, col2”) — AR still populates id: nil on the model objects).
- .evaluate(code) ⇒ Object
- .execute(code, original_question: nil, attempt: 1, message_id: nil) ⇒ Object
- .normalize(result) ⇒ Object
-
.normalize_hash(hash) ⇒ Object
Hashes from .group().count/sum/etc.
Class Method Details
.drop_all_nil_columns(rows) ⇒ Object
Removes columns where every row has a nil value (e.g. ‘id` when using .select(“col1, col2”) — AR still populates id: nil on the model objects).
83 84 85 86 87 88 89 90 |
# File 'lib/glancer/workflow/ar_executor.rb', line 83 def self.drop_all_nil_columns(rows) return rows if rows.empty? nil_cols = rows.first.keys.select { |k| rows.all? { |r| r[k].nil? } } return rows if nil_cols.empty? rows.map { |r| r.except(*nil_cols) } end |
.evaluate(code) ⇒ Object
46 47 48 |
# File 'lib/glancer/workflow/ar_executor.rb', line 46 def self.evaluate(code) TOPLEVEL_BINDING.eval(code) end |
.execute(code, original_question: nil, attempt: 1, message_id: nil) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/glancer/workflow/ar_executor.rb', line 6 def self.execute(code, original_question: nil, attempt: 1, message_id: nil) Glancer::Utils::Logger.info("Workflow::ARExecutor", "Executing AR expression (Attempt ##{attempt})...") run_id = SecureRandom.uuid begin result = nil Glancer::Utils::Transaction.make do |connection| Glancer::Workflow::Executor.apply_statement_timeout(connection) raw = evaluate(code) result = normalize(raw) raise ActiveRecord::Rollback end Glancer::Audit.create!( question: original_question, code: code, code_type: "activerecord", adapter: Glancer.configuration.resolved_adapter, run_id: run_id, executed_at: Time.current, message_id: ) result rescue StandardError => e if attempt >= 3 Glancer::Utils::Logger.error("Workflow::ARExecutor", "Final failure after #{attempt} attempts: #{e.}") return { error: true, message: e., last_code: code } end Glancer::Utils::Logger.warn("Workflow::ARExecutor", "AR Error (Attempt ##{attempt}): #{e.}. Requesting correction...") fixed_code = Glancer::Workflow::Builder.fix_ar_code(code, e.) execute(fixed_code, original_question: original_question, attempt: attempt + 1, message_id: ) end end |
.normalize(result) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/glancer/workflow/ar_executor.rb', line 50 def self.normalize(result) rows = case result when ActiveRecord::Relation result.to_a.map { |r| r.respond_to?(:attributes) ? r.attributes : { "value" => r } } when Array result.map do |item| if item.respond_to?(:attributes) item.attributes elsif item.is_a?(Hash) item.stringify_keys else { "value" => item } end end when Hash return normalize_hash(result.stringify_keys) when Numeric, String return [{ "result" => result }] when NilClass return [] else # Single AR model object (e.g. from .first / .find) return [{ "result" => result.inspect }] unless result.respond_to?(:attributes) [result.attributes] end drop_all_nil_columns(rows) end |
.normalize_hash(hash) ⇒ Object
Hashes from .group().count/sum/etc. map => aggregate and must be rendered as rows. Hashes where values are mixed types (e.g. model attributes) are kept as a single row.
95 96 97 98 99 100 101 |
# File 'lib/glancer/workflow/ar_executor.rb', line 95 def self.normalize_hash(hash) if hash.values.all? { |val| val.is_a?(Numeric) } hash.map { |key, val| { "key" => key.to_s, "value" => val } } else [hash] end end |