Module: ScopeHunter::ASTUtils
Constant Summary collapse
- AR_METHODS =
%i[where rewhere joins order limit offset select distinct group having references includes preload].freeze
Instance Method Summary collapse
- #const_name(node) ⇒ Object
- #hash_to_ruby(node) ⇒ Object
- #literal(node) ⇒ Object
- #model_const?(node) ⇒ Boolean
-
#relation_chain(node, require_model: true) ⇒ Object
Returns array of steps: ["User", msg: :where, args: [{status: :active]}, ...] Handles the compound
where.not(...)pattern as a single :where_not step. - #unwrap_args(args) ⇒ Object
- #where_not_node?(node) ⇒ Boolean
Instance Method Details
#const_name(node) ⇒ Object
64 65 66 67 68 |
# File 'lib/scope_hunter/ast_utils.rb', line 64 def const_name(node) node.const_name rescue NoMethodError nil end |
#hash_to_ruby(node) ⇒ Object
74 75 76 |
# File 'lib/scope_hunter/ast_utils.rb', line 74 def hash_to_ruby(node) node.pairs.to_h { |p| [literal(p.key), literal(p.value)] } end |
#literal(node) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/scope_hunter/ast_utils.rb', line 78 def literal(node) case when node.nil? then nil when node.sym_type? then node.value when node.str_type? then node.value when node.int_type? then node.value when node.float_type? then node.value when node.true_type? then true when node.false_type? then false when node.const_type? then node.const_name.to_sym else :__dynamic__ # lvar, ivar, send, etc. — normalized to ? by Canonicalizer, # enabling parameterized scope matching end end |
#model_const?(node) ⇒ Boolean
60 61 62 |
# File 'lib/scope_hunter/ast_utils.rb', line 60 def model_const?(node) node&.const_type? end |
#relation_chain(node, require_model: true) ⇒ Object
Returns array of steps: ["User", msg: :where, args: [{status: :active]}, ...]
Handles the compound where.not(...) pattern as a single :where_not step.
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 45 46 47 48 49 50 51 |
# File 'lib/scope_hunter/ast_utils.rb', line 13 def relation_chain(node, require_model: true) return nil unless node&.send_type? chain = [] cur = node seen_ar = false while cur&.send_type? msg = cur.method_name recv = cur.receiver args = cur.arguments if where_not_node?(cur) # `recv` is the bare `.where` call; its receiver is the model (or nil in scope body) model_recv = recv.receiver chain.unshift({ recv: model_recv&.const_type? ? const_name(model_recv) : nil, msg: :where_not, args: unwrap_args(args) }) seen_ar = true cur = model_recv elsif AR_METHODS.include?(msg) || model_const?(recv) chain.unshift({ recv: recv&.const_type? ? const_name(recv) : nil, msg: msg, args: unwrap_args(args) }) seen_ar ||= AR_METHODS.include?(msg) cur = recv else break end end return nil unless seen_ar return nil if require_model && chain.first[:recv].nil? # must start from Model constant chain end |
#unwrap_args(args) ⇒ Object
70 71 72 |
# File 'lib/scope_hunter/ast_utils.rb', line 70 def unwrap_args(args) args.map { |a| a.hash_type? ? hash_to_ruby(a) : literal(a) } end |
#where_not_node?(node) ⇒ Boolean
53 54 55 56 57 58 |
# File 'lib/scope_hunter/ast_utils.rb', line 53 def where_not_node?(node) node.method_name == :not && node.receiver&.send_type? && node.receiver.method_name == :where && node.receiver.arguments.empty? end |