Class: BazaModels::Query

Inherits:
Object
  • Object
show all
Includes:
Pagination
Defined in:
lib/baza_models/query.rb

Defined Under Namespace

Modules: Pagination Classes: Inspector, Not, SqlGenerator

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Pagination

#current_page, #out_of_bounds?, #page, #paginated?, #per, #total_entries, #total_pages

Constructor Details

#initialize(args) ⇒ Query

Returns a new instance of Query.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/baza_models/query.rb', line 10

def initialize(args)
  @args = args
  @model = @args[:model]
  @db = @model.db

  raise "No database?" unless @db

  @selects = args[:selects] || []
  @wheres = args[:wheres] || []
  @includes = args[:includes] || []
  @joins = args[:joins] || []
  @groups = args[:groups] || []
  @offset = args[:offset]
  @orders = args[:orders] || []
  @page = args[:page]
  @per = args[:per]
  @previous_model = args[:previous_model]
  @limit = args[:limit]

  @joins_tracker = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &blk) ⇒ Object (private)



433
434
435
436
437
438
439
440
441
# File 'lib/baza_models/query.rb', line 433

def method_missing(method_name, *args, &blk)
  return super unless @model

  scopes = @model.instance_variable_get(:@scopes)
  return super if !scopes || !scopes.key?(method_name)

  block = scopes.fetch(method_name).fetch(:blk)
  instance_exec(*args, &block)
end

Instance Attribute Details

#previous_modelObject

Returns the value of attribute previous_model.



8
9
10
# File 'lib/baza_models/query.rb', line 8

def previous_model
  @previous_model
end

#relationObject

Returns the value of attribute relation.



8
9
10
# File 'lib/baza_models/query.rb', line 8

def relation
  @relation
end

Instance Method Details

#<<(model) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/baza_models/query.rb', line 316

def <<(model)
  raise "No previous model set" unless @previous_model
  raise "No relation" unless @relation

  if model.persisted?
    model.update_attributes!(@relation.fetch(:foreign_key) => @previous_model.id)
  else
    autoloaded_cache_or_create << model
  end

  self
end

#<=(_other) ⇒ Object



334
335
336
# File 'lib/baza_models/query.rb', line 334

def <=(_other)
  false
end

#accessible_by(ability, action = :index) ⇒ Object

CanCan supports



330
331
332
# File 'lib/baza_models/query.rb', line 330

def accessible_by(ability, action = :index)
  ability.model_adapter(self, action).database_records
end

#allObject



32
33
34
# File 'lib/baza_models/query.rb', line 32

def all
  self
end

#any?Boolean

Returns:

  • (Boolean)


36
37
38
39
40
41
42
# File 'lib/baza_models/query.rb', line 36

def any?
  if @db.query(clone.select(:id).limit(1).to_sql).fetch
    true
  else
    false
  end
end

#countObject



52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/baza_models/query.rb', line 52

def count
  if @previous_model && @previous_model.new_record?
    autoloaded_cache_or_create.length
  else
    query = clone(selects: [])
      .select("COUNT(*) AS count")
      .limit(nil)
      .offset(nil)

    @db.query(query.to_sql).fetch.fetch(:count)
  end
end

#destroy_allObject



304
305
306
# File 'lib/baza_models/query.rb', line 304

def destroy_all
  each(&:destroy!)
end

#eachObject



264
265
266
267
268
# File 'lib/baza_models/query.rb', line 264

def each
  to_enum.each do |model|
    yield model
  end
end

#empty?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/baza_models/query.rb', line 44

def empty?
  !any?
end

#find(id) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
# File 'lib/baza_models/query.rb', line 89

def find(id)
  model = clone.where(id: id).limit(1).to_enum.first

  if model
    model.__send__(:fire_callbacks, :after_find)
  else
    raise BazaModels::Errors::RecordNotFound
  end

  model
end

#find_by(args) ⇒ Object



101
102
103
# File 'lib/baza_models/query.rb', line 101

def find_by(args)
  clone.where(args).limit(1).to_enum.first
end

#find_eachObject



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/baza_models/query.rb', line 270

def find_each
  query = clone
  query.instance_variable_set(:@order, [])
  query.instance_variable_set(:@limit, nil)
  query = query.order(:id)

  offset = 0

  loop do
    query = query.offset(offset, 1000)
    offset += 1000

    count = 0
    query.each do |model|
      yield model
      count += 1
    end

    break if count == 0
  end
end

#find_first(args) ⇒ Object



292
293
294
# File 'lib/baza_models/query.rb', line 292

def find_first(args)
  clone.where(args).first
end

#firstObject



105
106
107
108
109
110
111
112
113
114
# File 'lib/baza_models/query.rb', line 105

def first
  return autoloaded_cache.first if should_use_autoload?

  query = clone.limit(1)

  orders = query.instance_variable_get(:@orders)
  query = query.order(:id) if orders.empty?

  query.to_enum.first
end

#group(name) ⇒ Object



208
209
210
211
212
213
214
215
216
# File 'lib/baza_models/query.rb', line 208

def group(name)
  if name.is_a?(Symbol)
    clone(groups: @groups + ["`#{@model.table_name}`.`#{name}`"])
  elsif name.is_a?(String)
    clone(groups: @groups + [name])
  else
    raise "Didn't know how to group by that argument: #{name}"
  end
end

#includes(*names) ⇒ Object



145
146
147
# File 'lib/baza_models/query.rb', line 145

def includes(*names)
  clone(includes: @includes + names)
end

#inspectObject



312
313
314
# File 'lib/baza_models/query.rb', line 312

def inspect
  to_s
end

#joins(*arguments) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
# File 'lib/baza_models/query.rb', line 196

def joins(*arguments)
  BazaModels::Query::Inspector.new(
    query: self,
    model: @model,
    argument: arguments,
    joins: @joins,
    joins_tracker: @joins_tracker
  ).execute

  self
end

#lastObject



116
117
118
119
120
121
122
123
124
125
# File 'lib/baza_models/query.rb', line 116

def last
  return autoloaded_cache.last if should_use_autoload?

  query = clone.limit(1)

  orders = query.instance_variable_get(:@orders)
  query = query.order(:id) if orders.empty?

  query.reverse_order.to_enum.first
end

#lengthObject



65
66
67
68
69
70
71
# File 'lib/baza_models/query.rb', line 65

def length
  if @previous_model && !any_wheres_other_than_relation? && @previous_model.autoloads[@relation.fetch(:relation_name)]
    @previous_model.autoloads[@relation.fetch(:relation_name)].length
  else
    count
  end
end

#limit(limit) ⇒ Object



141
142
143
# File 'lib/baza_models/query.rb', line 141

def limit(limit)
  clone(limit: limit)
end

#map(&blk) ⇒ Object



233
234
235
# File 'lib/baza_models/query.rb', line 233

def map(&blk)
  to_enum.map(&blk)
end

#new(attributes) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/baza_models/query.rb', line 78

def new(attributes)
  raise "No previous model" unless @previous_model
  raise "No relation" unless @relation

  new_sub_model = @model.new(@relation.fetch(:foreign_key) => @previous_model.id)
  new_sub_model.assign_attributes(attributes)
  autoloaded_cache_or_create << new_sub_model

  new_sub_model
end

#none?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/baza_models/query.rb', line 48

def none?
  !any?
end

#offset(offset) ⇒ Object



137
138
139
# File 'lib/baza_models/query.rb', line 137

def offset(offset)
  clone(offset: offset)
end

#order(name) ⇒ Object



218
219
220
221
222
223
224
225
226
# File 'lib/baza_models/query.rb', line 218

def order(name)
  if name.is_a?(Symbol)
    clone(orders: @orders + ["`#{@model.table_name}`.`#{name}`"])
  elsif name.is_a?(String)
    clone(orders: @orders + [name])
  else
    raise "Didn't know how to order by that argument: #{name}"
  end
end

#ransack(params) ⇒ Object



343
344
345
# File 'lib/baza_models/query.rb', line 343

def ransack(params)
  BazaModels::Ransacker.new(class: @model, params: params, query: self)
end

#reverse_orderObject



228
229
230
231
# File 'lib/baza_models/query.rb', line 228

def reverse_order
  @reverse_order = true
  self
end

#sanitize_sql(value) ⇒ Object



338
339
340
341
# File 'lib/baza_models/query.rb', line 338

def sanitize_sql(value)
  return value if value.is_a?(Array) || value.is_a?(Integer) || value.is_a?(Integer)
  "'#{@db.esc(value)}'"
end

#select(select = nil, &blk) ⇒ Object



127
128
129
130
131
132
133
134
135
# File 'lib/baza_models/query.rb', line 127

def select(select = nil, &blk)
  if !select && blk
    to_enum.select(&blk)
  elsif select.is_a?(Symbol)
    clone(selects: @selects + ["`#{@model.table_name}`.`#{select}`"])
  else
    clone(selects: @selects + [select])
  end
end

#sizeObject



73
74
75
76
# File 'lib/baza_models/query.rb', line 73

def size
  # TODO: This should also take counter caching into account
  length
end

#to_aObject



296
297
298
# File 'lib/baza_models/query.rb', line 296

def to_a
  to_enum.to_a
end

#to_enumObject



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/baza_models/query.rb', line 237

def to_enum
  return autoloaded_cache if should_use_autoload?

  array_enum = ArrayEnumerator.new do |yielder|
    @db.query(to_sql).each do |data|
      yielder << @model.new(data, init: true)
    end
  end

  if @includes.empty?
    return array_enum
  else
    array = array_enum.to_a

    if @includes.any? && array.any?
      autoloader = BazaModels::Autoloader.new(
        models: array,
        autoloads: @includes,
        db: @db
      )
      autoloader.autoload
    end

    return array
  end
end

#to_sObject



308
309
310
# File 'lib/baza_models/query.rb', line 308

def to_s
  "#<BazaModels::Query class=#{@model.name} wheres=#{@wheres}>"
end

#to_sqlObject



300
301
302
# File 'lib/baza_models/query.rb', line 300

def to_sql
  BazaModels::Query::SqlGenerator.new(query: self).to_sql
end

#where(*args) ⇒ Object



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
188
189
190
191
192
193
194
# File 'lib/baza_models/query.rb', line 149

def where(*args)
  first_arg = args.first
  new_wheres = @wheres.dup

  if first_arg.is_a?(String)
    new_where = "(#{args.shift})"

    args.each do |arg|
      new_where.sub!("?", @db.sqlval(arg))
    end

    new_wheres << new_where
  elsif first_arg.is_a?(Array)
    str = first_arg.shift

    first_arg.each do |arg|
      if arg.is_a?(Symbol)
        arg = "`#{@model.table_name}`.`#{@db.escape_column(arg)}`"
      elsif arg.is_a?(FalseClass)
        arg = "0"
      elsif arg.is_a?(TrueClass)
        arg = "1"
      else
        arg = @db.sqlval(arg)
      end

      str.sub!("?", arg)
    end

    new_wheres << "(#{str})"
  elsif first_arg == nil
    return Not.new(query: self)
  else
    first_arg.each do |key, value|
      if value.is_a?(Hash)
        value.each do |hash_key, hash_value|
          new_wheres << "`#{key}`.`#{key_convert(hash_key, hash_value)}` #{value_with_mode(value_convert(hash_value))}"
        end
      else
        new_wheres << "`#{@model.table_name}`.`#{key_convert(key, value)}` #{value_with_mode(value_convert(value))}"
      end
    end
  end

  clone(wheres: new_wheres)
end