Class: Huginn::Datatable::AssociationPath
- Inherits:
-
Object
- Object
- Huginn::Datatable::AssociationPath
- Defined in:
- lib/huginn/datatable/association_path.rb
Overview
Resolves a multi-segment association chain ("company.people.name") into the concrete steps (tables, klasses and Arel link conditions) needed to build:
* a subquery for filtering/ranges
WHERE pk IN (SELECT DISTINCT pk FROM <chain> join ... WHERE ...)
* a correlated scalar subquery for ordering
ORDER BY (SELECT col FROM <chain> join ... WHERE <correlation> ...)
The link conditions are direction aware: a belongs_to puts the FK on
the owner table while has_many/has_one put it on the child table.
Association scopes are folded in as Arel predicates (never parsed SQL).
Defined Under Namespace
Classes: Step
Instance Attribute Summary collapse
-
#column ⇒ Object
readonly
Returns the value of attribute column.
-
#segments ⇒ Object
readonly
Returns the value of attribute segments.
Class Method Summary collapse
- .call(model, field) ⇒ Object
-
.join_spec_for(names) ⇒ Object
Reverse engineers a join spec usable by
ActiveRecord::Relation#joinsfor filter subqueries:.joins(:people: { company: ... }).
Instance Method Summary collapse
-
#association_names ⇒ Object
The association steps of the chain ("company", "people").
-
#correlated_order_expression ⇒ Object
Correlated scalar subquery: ORDER BY (SELECT
FROM ... WHERE [AND scope...] ORDER BY ASC LIMIT 1). -
#initialize(model, field) ⇒ AssociationPath
constructor
A new instance of AssociationPath.
-
#resolved_steps ⇒ Object
The walkable Arel filter/order chain, or nil when invalid.
-
#scope_constraints ⇒ Object
The Arel predicates folded in by the association scopes of every step.
- #target_klass ⇒ Object
- #target_table ⇒ Object
- #valid? ⇒ Boolean
Constructor Details
#initialize(model, field) ⇒ AssociationPath
Returns a new instance of AssociationPath.
27 28 29 30 31 |
# File 'lib/huginn/datatable/association_path.rb', line 27 def initialize(model, field) @model = model @segments = field.to_s.split(".") @column = segments.last end |
Instance Attribute Details
#column ⇒ Object (readonly)
Returns the value of attribute column.
21 22 23 |
# File 'lib/huginn/datatable/association_path.rb', line 21 def column @column end |
#segments ⇒ Object (readonly)
Returns the value of attribute segments.
21 22 23 |
# File 'lib/huginn/datatable/association_path.rb', line 21 def segments @segments end |
Class Method Details
.call(model, field) ⇒ Object
23 24 25 |
# File 'lib/huginn/datatable/association_path.rb', line 23 def self.call(model, field) new(model, field) end |
.join_spec_for(names) ⇒ Object
Reverse engineers a join spec usable by ActiveRecord::Relation#joins
for filter subqueries: .joins(:people: { company: ... }).
88 89 90 91 92 |
# File 'lib/huginn/datatable/association_path.rb', line 88 def self.join_spec_for(names) names.reverse.reduce(nil) do |acc, name| acc.nil? ? name.to_sym : { name.to_sym => acc } end end |
Instance Method Details
#association_names ⇒ Object
The association steps of the chain ("company", "people").
34 35 36 |
# File 'lib/huginn/datatable/association_path.rb', line 34 def association_names resolved_steps.map { |step| step.reflection.name.to_s } end |
#correlated_order_expression ⇒ Object
Correlated scalar subquery: ORDER BY (SELECT
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/huginn/datatable/association_path.rb', line 58 def steps = resolved_steps return nil unless steps manager = Arel::SelectManager.new manager.from(steps.first.table) steps.drop(1).each do |step| manager.join(step.table).on(step.link_to_previous) end # The first step correlates the subquery to the outer (base) table. manager.where(steps.first.link_to_previous) scope_predicates.each { |predicate| manager.where(predicate) } manager.project(target_table[column]) manager.order(target_table[column].asc) manager.take(1) Arel.sql("(#{manager.to_sql})") rescue StandardError nil end |
#resolved_steps ⇒ Object
The walkable Arel filter/order chain, or nil when invalid.
51 52 53 |
# File 'lib/huginn/datatable/association_path.rb', line 51 def resolved_steps @steps ||= build_steps end |
#scope_constraints ⇒ Object
The Arel predicates folded in by the association scopes of every step. Public so subquery builders can replicate ActiveRecord's scoped joins.
82 83 84 |
# File 'lib/huginn/datatable/association_path.rb', line 82 def scope_constraints scope_predicates end |
#target_klass ⇒ Object
38 39 40 |
# File 'lib/huginn/datatable/association_path.rb', line 38 def target_klass resolved_steps.last.klass end |
#target_table ⇒ Object
42 43 44 |
# File 'lib/huginn/datatable/association_path.rb', line 42 def target_table resolved_steps.last.table end |
#valid? ⇒ Boolean
46 47 48 |
# File 'lib/huginn/datatable/association_path.rb', line 46 def valid? resolved_steps.present? end |