Module: UsefulUtilities::AR

Extended by:
AR
Included in:
AR
Defined in:
lib/useful_utilities/ar.rb

Overview

ActiveRecord utilities

Defined Under Namespace

Modules: Methods

Constant Summary collapse

NESTED_ASSOCIATIONS =
%i( has_one has_many )
BASE_ERROR_KEY =
:base
TYPE_SUFFIX =
'%s_type'
ID_SUFFIX =
'%s_id'

Instance Method Summary collapse

Instance Method Details

#[](klass, *columns) ⇒ String

Returns qualified columns splitted by comma.

Parameters:

  • klass (Class)
  • columns

    list of columns

Returns:

  • (String)

    qualified columns splitted by comma



114
115
116
# File 'lib/useful_utilities/ar.rb', line 114

def [](klass, *columns)
  columns.map { |column| "#{ klass.table_name }.#{ column }" }.join(', ')
end

#asc(klass, column) ⇒ String

Returns ASC statement for ORDER BY.

Parameters:

  • klass (Class)
  • column (String/Symbol)

Returns:

  • (String)

    ASC statement for ORDER BY



121
122
123
# File 'lib/useful_utilities/ar.rb', line 121

def asc(klass, column)
  "#{ self[klass, column] } ASC"
end

#boolean_to_float(value) ⇒ Float

Returns value as float.

Examples:

UsefulUtilities::AR.boolean_to_float(false) #=> 0.0
UsefulUtilities::AR.boolean_to_float(true)  #=> 1.0

Parameters:

  • value

Returns:

  • (Float)

    value as float



58
59
60
# File 'lib/useful_utilities/ar.rb', line 58

def boolean_to_float(value)
  value ? 1.0 : 0.0
end

#by_polymorphic(scope, key, value) ⇒ ActiveRecord::Relation

Parameters:

  • scope (ActiveRecord::Relation)
  • key (Symbol)
  • value

    value to search

Returns:

  • (ActiveRecord::Relation)


92
93
94
95
96
# File 'lib/useful_utilities/ar.rb', line 92

def by_polymorphic(scope, key, value)
  type = to_polymorphic_type(value)
  type_column, id_column = TYPE_SUFFIX % key, ID_SUFFIX % key
  scope.where(type_column => type, id_column => value)
end

#ceil(value) ⇒ Object

Returns CEILING statement.

Parameters:

  • value

Returns:

  • CEILING statement



156
157
158
# File 'lib/useful_utilities/ar.rb', line 156

def ceil(value)
  "CEILING(#{value})"
end

#count(klass, column, result) ⇒ String

Returns count statement.

Parameters:

  • klass (Class)
  • column (String/Symbol)
  • result (String/Symbol)

    alias

Returns:

  • (String)

    count statement



191
192
193
# File 'lib/useful_utilities/ar.rb', line 191

def count(klass, column, result)
  "COUNT(#{ self[klass, column] }) AS #{ result }"
end

#deep_validation(record) ⇒ Object

Validates record and associations

Parameters:

  • record


64
65
66
67
# File 'lib/useful_utilities/ar.rb', line 64

def deep_validation(record)
  record.valid?
  nested_associations_validation(record)
end

#delete_dependents(owner, self_class, dependent_class) ⇒ Object

Deletes dependent records

Parameters:

  • owner
  • self_class (Class)
  • dependent_class (Class)


199
200
201
202
203
204
205
206
207
208
# File 'lib/useful_utilities/ar.rb', line 199

def delete_dependents(owner, self_class, dependent_class)
  dependent_fk = dependent_class.foreign_key(self_class)
  self_fk = self_class.foreign_key(owner.class)

     "DELETE #{dependent_class.table_name}
        FROM #{dependent_class.table_name}
  INNER JOIN #{self_class.table_name}
          ON #{self[dependent_class, dependent_fk]} = #{self[self_class, :id]}
       WHERE #{self[self_class, self_fk]} = #{owner.id}"
end

#desc(klass, column) ⇒ String

Returns DESC statement for ORDER BY.

Parameters:

  • klass (Class)
  • column (String/Symbol)

Returns:

  • (String)

    DESC statement for ORDER BY



128
129
130
# File 'lib/useful_utilities/ar.rb', line 128

def desc(klass, column)
  "#{ self[klass, column] } DESC"
end

#nested_associations_validation(record, nested = false) ⇒ Object

Validates nested associations of a record

Parameters:

  • record
  • nested (Boolean) (defaults to: false)


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/useful_utilities/ar.rb', line 72

def nested_associations_validation(record, nested = false)
  record.class.reflections.each do |name, reflection|
    next unless NESTED_ASSOCIATIONS.include?(reflection.macro)

    association_records = Array(record.public_send(name))
    error_key = nested ? BASE_ERROR_KEY : name
    record.errors.delete(name)

    association_records.each do |nested_record|
      next unless nested_record.changed?
      nested_associations_validation(nested_record, :nested)
      nested_record.errors.full_messages.each { |message| record.errors[error_key] << message }
    end
  end
end

#null_to_zero(*args) ⇒ Object

Returns COALESCE statement.

Parameters:

  • args

    list of columns

Returns:

  • COALESCE statement



142
143
144
145
146
147
148
149
150
151
152
# File 'lib/useful_utilities/ar.rb', line 142

def null_to_zero(*args)
  sql_column =
    case args.size
    when 1 then args.first
    when 2 then self[args[0], args[1]]
    else
      raise ArgumentError, "wrong number of arguments (#{ args.size } for 1..2)"
    end

  "COALESCE(#{ sql_column }, 0)"
end

#older_than(scope, _value) ⇒ ActiveRecord::Relation

Parameters:

  • scope (ActiveRecord::Relation)
  • _value

    class or column

Returns:

  • (ActiveRecord::Relation)


135
136
137
138
# File 'lib/useful_utilities/ar.rb', line 135

def older_than(scope, _value)
  value = _value.is_a?(ActiveRecord::Base) ? _value.id : _value
  value.present? ? scope.where("#{ self[scope, :id] } < ?", value) : scope
end

#sql_sum(*args, result) ⇒ Object

Returns SUM statement.

Parameters:

  • args
  • result

    alias

Returns:

  • SUM statement



163
164
165
# File 'lib/useful_utilities/ar.rb', line 163

def sql_sum(*args, result)
  "SUM(#{ null_to_zero(*args) }) AS #{ result }"
end

#sum_by_columns(scope, *all_columns) ⇒ Object

Can be used for not NULL columns only sum_by_columns(Virtual, :column_1, :column2, column3: :column3_alias…)

Parameters:

  • scope (ActiveRecord::Relation)
  • all_columns

    list of columns



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/useful_utilities/ar.rb', line 171

def sum_by_columns(scope, *all_columns)
  columns = all_columns.flatten
  columns_with_aliases = columns.extract_options!

  sql_query =
    columns.reduce(columns_with_aliases) do |res, column|
      res.merge!(column => column)
    end.
    map do |column, column_alias|
      "COALESCE(SUM(#{ column }), 0) AS #{ column_alias }"
    end.
    join(', ')

  scope.select(sql_query).first
end

#to_polymorphic_type(value) ⇒ Object

Parameters:

  • value

Returns:

  • value if value does not respond to base_class

  • (Class)

    base class



101
102
103
104
105
106
107
108
109
# File 'lib/useful_utilities/ar.rb', line 101

def to_polymorphic_type(value)
  klass =
    if value.is_a?(Class) || value.nil? then value
    elsif value.respond_to?(:klass) then value.klass
    else value.class
    end

  klass.respond_to?(:base_class) ? klass.base_class : klass
end

#value_to_boolean(value) ⇒ Boolean

Returns value as boolean.

Parameters:

  • value

Returns:

  • (Boolean)

    value as boolean



47
48
49
50
51
# File 'lib/useful_utilities/ar.rb', line 47

def value_to_boolean(value)
  return false if value.nil?

  ActiveRecord::Type::Boolean.new.type_cast_from_database(value)
end

#value_to_decimal(value) ⇒ BigDecimal

Returns value as BigDecimal.

Parameters:

  • value

Returns:

  • (BigDecimal)

    value as BigDecimal



39
40
41
42
43
# File 'lib/useful_utilities/ar.rb', line 39

def value_to_decimal(value)
  return BigDecimal.new(0) if value.nil?

  ActiveRecord::Type::Decimal.new.type_cast_from_database(value)
end

#value_to_integer(value) ⇒ Integer

Returns value as Integer.

Parameters:

  • value

Returns:

  • (Integer)

    value as Integer



29
30
31
32
33
34
35
# File 'lib/useful_utilities/ar.rb', line 29

def value_to_integer(value)
  return 0 if value.nil?
  return 0 if value == false
  return 1 if value == true

  ActiveRecord::Type::Integer.new.type_cast_from_database(value)
end