Class: Huginn::Datatable::Validator

Inherits:
Object
  • Object
show all
Defined in:
lib/huginn/datatable/validator.rb

Overview

Resolves and validates column references used by datatable filters, orders and ranges.

A field is either a plain column ("status"), an association-scoped column ("pessoa.nome", "company.people.age") or — when the model declares huginn_attributes — a public API alias that maps to the real column.

Fields are never interpolated into SQL strings: scoped fields are resolved through AssociationPath (direction aware FK/PK linking) and authorization is enforced against the allowed_paths allowlist. When the model configures huginn_attributes(strict: true) only the declared aliases are accepted, so the schema stays hidden from callers.

Constant Summary collapse

FIELD_PATTERN =
/\A[a-z_][a-z0-9_]*\z/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model, field) ⇒ Validator

Returns a new instance of Validator.



25
26
27
28
29
30
# File 'lib/huginn/datatable/validator.rb', line 25

def initialize(model, field)
  @model = model
  @strict_denied = false
  @field = resolve_alias(field)
  @field = @field.split(".").drop(1).join(".") if own_table_prefix?(@field)
end

Class Method Details

.call(model, field) ⇒ Object



21
22
23
# File 'lib/huginn/datatable/validator.rb', line 21

def self.call(model, field)
  new(model, field)
end

Instance Method Details

#allowlist_keyObject



95
96
97
98
99
100
# File 'lib/huginn/datatable/validator.rb', line 95

def allowlist_key
  return nil if plain?

  joined = authorization_path.join(".")
  joined.empty? ? nil : joined
end

#arel_attributeObject



64
65
66
67
68
# File 'lib/huginn/datatable/validator.rb', line 64

def arel_attribute
  return nil unless valid?

  plain? ? model.arel_table[column] : association_path.target_table[column]
end

#association_nameObject

First association name of the chain (backwards compatible alias used by specs), or nil for plain columns.



85
86
87
88
89
# File 'lib/huginn/datatable/validator.rb', line 85

def association_name
  return nil if plain?

  association_path&.association_names&.first&.to_sym
end

#association_pathObject

The AssociationPath for scoped fields (builds filter/order subqueries), or nil for plain columns.



79
80
81
# File 'lib/huginn/datatable/validator.rb', line 79

def association_path
  @association_path ||= (AssociationPath.call(model, @field) if scoped?)
end

#authorization_pathObject

Canonical chain of association reflection names (e.g. ["company", "people"]) used to match the allowlist. Table names and public aliases are normalized through the reflection.



58
59
60
61
62
# File 'lib/huginn/datatable/validator.rb', line 58

def authorization_path
  return [] if plain?

  association_path&.association_names || []
end

#column_typeObject



70
71
72
73
74
75
# File 'lib/huginn/datatable/validator.rb', line 70

def column_type
  return nil unless valid?

  klass = plain? ? model : association_path.target_klass
  klass.columns_hash[column].type
end

#correlated_order_expressionObject



91
92
93
# File 'lib/huginn/datatable/validator.rb', line 91

def correlated_order_expression
  association_path&.correlated_order_expression
end

#plain?Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/huginn/datatable/validator.rb', line 36

def plain?
  segments.size == 1
end

#scoped?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/huginn/datatable/validator.rb', line 40

def scoped?
  !plain? && segments.size >= 2
end

#segmentsObject



32
33
34
# File 'lib/huginn/datatable/validator.rb', line 32

def segments
  @segments ||= @field.split(".")
end

#valid?Boolean

Returns:

  • (Boolean)


44
45
46
47
48
49
50
51
52
53
# File 'lib/huginn/datatable/validator.rb', line 44

def valid?
  return false if strict_denied?
  return false unless segments.all? { |segment| segment.match?(FIELD_PATTERN) }

  if plain?
    model.columns_hash.key?(column)
  else
    association_path&.valid? && association_path.target_klass.columns_hash.key?(column)
  end
end