Class: ActiveRecord::Base

Inherits:
Object show all
Defined in:
lib/brick/extensions.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

._assoc_namesObject



66
67
68
# File 'lib/brick/extensions.rb', line 66

def self._assoc_names
  @_assoc_names ||= {}
end

._br_associativesObject

has_many :through associative tables



291
292
293
# File 'lib/brick/extensions.rb', line 291

def _br_associatives
  @_br_associatives ||= {}
end

._br_bt_descripObject

belongs_to DSL descriptions



283
284
285
# File 'lib/brick/extensions.rb', line 283

def _br_bt_descrip
  @_br_bt_descrip ||= {}
end

._br_cust_colsObject

Custom columns



295
296
297
# File 'lib/brick/extensions.rb', line 295

def _br_cust_cols
  @_br_cust_cols ||= {}
end

._br_hm_countsObject

has_many count definitions



287
288
289
# File 'lib/brick/extensions.rb', line 287

def _br_hm_counts
  @_br_hm_counts ||= {}
end

._brick_calculate_bts_hms(translations, join_array) ⇒ Object

Search for custom column, BT, HM, and HMT DSL stuff



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/brick/extensions.rb', line 301

def self._brick_calculate_bts_hms(translations, join_array)
  # Add any custom columns
  ::Brick.config.custom_columns&.fetch(table_name, nil)&.each do |k, cc|
    # false = not polymorphic, and true = yes -- please emit_dsl
    pieces, my_dsl = brick_parse_dsl(join_array, [], translations, false, cc, true)
    _br_cust_cols[k] = [pieces, my_dsl]
  end
  bts, hms, associatives = ::Brick.get_bts_and_hms(self)
  bts.each do |_k, bt|
    next if bt[2] # Polymorphic?

    # join_array will receive this relation name when calling #brick_parse_dsl
    _br_bt_descrip[bt.first] = if bt[1].is_a?(Array)
                                 # Last params here:  "true" is for yes, we are polymorphic
                                 bt[1].each_with_object({}) { |bt_class, s| s[bt_class] = bt_class.brick_parse_dsl(join_array, bt.first, translations, true) }
                               else
                                 { bt.last => bt[1].brick_parse_dsl(join_array, bt.first, translations) }
                               end
  end
  skip_klass_hms = ::Brick.config.skip_index_hms[self.name] || {}
  hms.each do |k, hm|
    next if skip_klass_hms.key?(k)

    if hm.macro == :has_one
      # For our purposes a :has_one is similar enough to a :belongs_to that we can just join forces
      _br_bt_descrip[k] = { hm.klass => hm.klass.brick_parse_dsl(join_array, k, translations) }
    else # Standard :has_many
      _br_hm_counts[k] = hm unless hm.options[:through] && !_br_associatives.fetch(hm.name, nil)
    end
  end
end

._brick_calculate_ordering(ordering, is_do_txt = true) ⇒ Object



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/brick/extensions.rb', line 333

def self._brick_calculate_ordering(ordering, is_do_txt = true)
  quoted_table_name = table_name.split('.').map { |x| "\"#{x}\"" }.join('.')
  order_by_txt = [] if is_do_txt
  ordering = [ordering] if ordering && !ordering.is_a?(Array)
  order_by = ordering&.each_with_object([]) do |ord_part, s| # %%% If a term is also used as an eqi-condition in the WHERE clause, it can be omitted from ORDER BY
               case ord_part
               when String
                 ord_expr = ord_part.gsub('^^^', quoted_table_name)
                 order_by_txt&.<<("Arel.sql(#{ord_expr})")
                 s << Arel.sql(ord_expr)
               else # Expecting only Symbol
                 if _br_hm_counts.key?(ord_part)
                   ord_part = "\"b_r_#{ord_part}_ct\""
                 elsif !_br_bt_descrip.key?(ord_part) && !_br_cust_cols.key?(ord_part) && !column_names.include?(ord_part.to_s)
                   # Disallow ordering by a bogus column
                   # %%% Note this bogus entry so that Javascript can remove any bogus _brick_order
                   # parameter from the querystring, pushing it into the browser history.
                   ord_part = nil
                 end
                 if ord_part
                   # Retain any reference to a bt_descrip as being a symbol
                   # Was:  "#{quoted_table_name}.\"#{ord_part}\""
                   order_by_txt&.<<(_br_bt_descrip.key?(ord_part) ? ord_part : ord_part.inspect)
                   s << ord_part
                 end
               end
             end
  [order_by, order_by_txt]
end

._brick_index(mode = nil) ⇒ Object



263
264
265
266
267
268
269
270
271
# File 'lib/brick/extensions.rb', line 263

def self._brick_index(mode = nil)
  tbl_parts = ((mode == :singular) ? table_name.singularize : table_name).split('.')
  tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == Apartment.default_schema
  tbl_parts.unshift(::Brick.config.path_prefix) if ::Brick.config.path_prefix
  index = tbl_parts.map(&:underscore).join('_')
  # Rails applies an _index suffix to that route when the resource name is singular
  index << '_index' if mode != :singular && index == index.singularize
  index
end

._brick_primary_key(relation = nil) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/brick/extensions.rb', line 74

def self._brick_primary_key(relation = nil)
  return @_brick_primary_key if instance_variable_defined?(:@_brick_primary_key)

  pk = begin
         primary_key.is_a?(String) ? [primary_key] : primary_key || []
       rescue
         []
       end
  pk.map! { |pk_part| pk_part =~ /^[A-Z0-9_]+$/ ? pk_part.downcase : pk_part } unless connection.adapter_name == 'MySQL2'
  # Just return [] if we're missing any part of the primary key.  (PK is usually just "id")
  if relation && pk.present?
    @_brick_primary_key ||= pk.any? { |pk_part| !relation[:cols].key?(pk_part) } ? [] : pk
  else # No definitive key yet, so return what we can without setting the instance variable
    pk
  end
end

.brick_descrip(obj, data = nil, pk_alias = nil) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/brick/extensions.rb', line 196

def self.brick_descrip(obj, data = nil, pk_alias = nil)
  dsl = obj if obj.is_a?(String)
  if (dsl ||= ::Brick.config.model_descrips[(klass = self).name] || klass.brick_get_dsl)
    idx = -1
    caches = {}
    output = +''
    is_brackets_have_content = false
    bracket_name = nil
    dsl.each_char do |ch|
      if bracket_name
        if ch == ']' # Time to process a bracketed thing?
          datum = if data
                    data[idx += 1].to_s
                  else
                    obj_name = +''
                    this_obj = obj
                    bracket_name.split('.').each do |part|
                      obj_name += ".#{part}"
                      this_obj = caches.fetch(obj_name) { caches[obj_name] = this_obj&.send(part.to_sym) }
                      break if this_obj.nil?
                    end
                    if this_obj.is_a?(ActiveRecord::Base) && (obj_descrip = this_obj.class.brick_descrip(this_obj))
                      this_obj = obj_descrip
                    end
                    this_obj&.to_s || ''
                  end
          is_brackets_have_content = true unless datum.blank?
          output << (datum || '')
          bracket_name = nil
        else
          bracket_name << ch
        end
      elsif ch == '['
        bracket_name = +''
      else
        output << ch
      end
    end
    output += bracket_name if bracket_name
  end
  if is_brackets_have_content
    output
  elsif (pk_alias ||= primary_key)
    pk_alias = [pk_alias] unless pk_alias.is_a?(Array)
    id = []
    pk_alias.each do |pk_alias_part|
      if (pk_part = obj.send(pk_alias_part))
        id << pk_part
      end
    end
    if id.present?
      "#{klass.name} ##{id.join(', ')}"
    end
  else
    obj.to_s
  end
end

.brick_get_dslObject

Used to show a little prettier name for an object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/brick/extensions.rb', line 92

def self.brick_get_dsl
  # If there's no DSL yet specified, just try to find the first usable column on this model
  unless (dsl = ::Brick.config.model_descrips[name])
    descrip_col = (columns.map(&:name) - _brick_get_fks -
                  (::Brick.config. || []) -
                  [primary_key]).first
    dsl = ::Brick.config.model_descrips[name] = if descrip_col
                                                  "[#{descrip_col}]"
                                                elsif (pk_parts = self.primary_key.is_a?(Array) ? self.primary_key : [self.primary_key])
                                                  "#{name} ##{pk_parts.map { |pk_part| "[#{pk_part}]" }.join(', ')}"
                                                end
  end
  dsl
end

.brick_import_templateObject



273
274
275
276
277
278
279
# File 'lib/brick/extensions.rb', line 273

def self.brick_import_template
  template = constants.include?(:IMPORT_TEMPLATE) ? self::IMPORT_TEMPLATE : suggest_template(false, false, 0)
  # Add the primary key to the template as being unique (unless it's already there)
  template[:uniques] = [pk = primary_key.to_sym]
  template[:all].unshift(pk) unless template[:all].include?(pk)
  template
end

.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false, dsl = nil, emit_dsl = false) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/brick/extensions.rb', line 107

def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {}, is_polymorphic = false, dsl = nil, emit_dsl = false)
  unless build_array.is_a?(::Brick::JoinArray)
    build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
    build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
  end
  prefix = [prefix] unless prefix.is_a?(Array)
  members = []
  unless dsl || (dsl = ::Brick.config.model_descrips[name] || brick_get_dsl)
    # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
    x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
    x[prefix.last] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
    return members
  end

  # Do the actual dirty work of recursing through nested DSL
  bracket_name = nil
  dsl2 = +'' # To replace our own DSL definition in case it needs to be expanded
  dsl3 = +'' # To return expanded DSL that is nested from another model
  klass = nil
  dsl.each_char do |ch|
    if bracket_name
      if ch == ']' # Time to process a bracketed thing?
        parts = bracket_name.split('.')
        first_parts = parts[0..-2].map do |part|
          klass = (orig_class = klass).reflect_on_association(part_sym = part.to_sym)&.klass
          puts "Couldn't reference #{orig_class.name}##{part} that's part of the DSL \"#{dsl}\"." if klass.nil?
          part_sym
        end
        parts = prefix + first_parts + [parts[-1]]
        if parts.length > 1
          unless is_polymorphic
            s = build_array
            parts[0..-3].each { |v| s = s[v.to_sym] }
            s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
          end
          translations[parts[0..-2].join('.')] = klass
        end
        if klass.column_names.exclude?(parts.last) &&
           (klass = (orig_class = klass).reflect_on_association(possible_dsl = parts.pop.to_sym)&.klass)
          if prefix.empty? # Custom columns start with an empty prefix
            prefix << parts.shift until parts.empty?
          end
          # Expand this entry which refers to an association name
          members2, dsl2a = klass.brick_parse_dsl(build_array, prefix + [possible_dsl], translations, is_polymorphic, nil, true)
          members += members2
          dsl2 << dsl2a
          dsl3 << dsl2a
        else
          dsl2 << "[#{bracket_name}]"
          if emit_dsl
            dsl3 << "[#{prefix[1..-1].map { |p| "#{p.to_s}." }.join if prefix.length > 1}#{bracket_name}]"
          end
          members << parts
        end
        bracket_name = nil
      else
        bracket_name << ch
      end
    elsif ch == '['
      bracket_name = +''
      klass = self
    else
      dsl2 << ch
      dsl3 << ch
    end
  end
  # Rewrite the DSL in case it's now different from having to expand it
  # if ::Brick.config.model_descrips[name] != dsl2
  #   puts ::Brick.config.model_descrips[name]
  #   puts dsl2.inspect
  #   puts dsl3.inspect
  #   binding.pry
  # end
  if emit_dsl
    # Had been:  [members, dsl2, dsl3]
    [members, dsl3]
  else
    ::Brick.config.model_descrips[name] = dsl2
    members
  end
end

.brick_select(params = {}, selects = [], *args) ⇒ Object



363
364
365
366
# File 'lib/brick/extensions.rb', line 363

def self.brick_select(params = {}, selects = [], *args)
  (relation = all).brick_select(params, selects, *args)
  relation.select(selects)
end


254
255
256
257
258
259
260
261
# File 'lib/brick/extensions.rb', line 254

def self.bt_link(assoc_name)
  assoc_name = CGI.escapeHTML(assoc_name.to_s)
  model_path = ::Rails.application.routes.url_helpers.send("#{_brick_index}_path".to_sym)
  av_class = Class.new.extend(ActionView::Helpers::UrlHelper)
  av_class.extend(ActionView::Helpers::TagHelper) if ActionView.version < ::Gem::Version.new('7')
  link = av_class.link_to(name, model_path)
  table_name == assoc_name ? link : "#{assoc_name}-#{link}".html_safe
end

.is_brick?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/brick/extensions.rb', line 62

def self.is_brick?
  instance_variables.include?(:@_brick_built) && instance_variable_get(:@_brick_built)
end

.is_view?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/brick/extensions.rb', line 70

def self.is_view?
  false
end

Instance Method Details

#brick_descrip(data = nil, pk_alias = nil) ⇒ Object

If available, parse simple DSL attached to a model in order to provide a friendlier name. Object property names can be referenced in square brackets like this: { ‘User’ => ‘[profile.firstname] [profile.lastname]’ }



192
193
194
# File 'lib/brick/extensions.rb', line 192

def brick_descrip(data = nil, pk_alias = nil)
  self.class.brick_descrip(self, data, pk_alias)
end