Class: LegacyMigrations::Squirrel::Query::ConditionGroup

Inherits:
Object
  • Object
show all
Defined in:
lib/legacy_migrations/squirrel/squirrel.rb

Overview

ConditionGroups are groups of Conditions, oddly enough. They most closely map to models in your schema, but they also handle the grouping jobs for the #any and #all blocks.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model, logical_join, binding, from_record = nil, path = nil, reflection = nil, &blk) ⇒ ConditionGroup

Creates a ConditionGroup by passing in the following arguments:

  • model: The AR subclass that defines what columns and associations will be accessible

in the given block.

  • logical_join: A string containing the join that will be used when concatenating the

conditions together. The root level ConditionGroup created by Query defaults the join to be “AND”, but the #any and #all methods will create specific ConditionGroups using “OR” and “AND” as their join, respectively.

  • binding: The binding of the block passed to the original #find. Will be used to

eval what self would be. This is necessary for using methods like params and session in your controllers.

  • path: The “path” taken through the models to arrive at this model. For example, if

your User class has_many Posts which has_many Comments each of which belongs_to User, the path to the second User would be [:posts, :comments, :user]

  • reflection: The association used to get to this block. If nil, then no new association

was traversed, which means we’re in an #any or #all grouping block.

  • blk: The block to be executed.

This method defines a number of methods to be available inside the block, one for each of the columns and associations in the specified model. Note that you CANNOT use user-defined methods on your model inside Squirrel queries. They don’t have any meaning in the context of a database query.



190
191
192
193
194
195
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
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 190

def initialize model, logical_join, binding, from_record = nil, path = nil, reflection = nil, &blk
  @model = model
  @from = from_record
  @logical_join = logical_join
  @conditions = []
  @condition_blocks = []
  @reflection = reflection
  @path = [ path, reflection ].compact.flatten
  @binding = binding
  @order = []
  @negative = false
  @paginator = false
  @block = blk
   
  existing_methods = self.class.instance_methods(false)
  (model.column_names - existing_methods).each do |col|
    (class << self; self; end).class_eval do
      define_method(col.to_s.intern) do
        column(col)
      end
    end
  end
  (model.reflections.keys - existing_methods).each do |assn|
    (class << self; self; end).class_eval do
      define_method(assn.to_s.intern) do
        association(assn)
      end
    end
  end
   
  execute_block
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object (private)

:nodoc:



410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 410

def method_missing meth, *args #:nodoc:
  m = eval <<-end_eval, binding
  begin
  method(:#{meth})
  rescue NameError
  nil
  end
  end_eval
  if m
    m.call(*args)
  else
    super(meth, *args)
  end
end

Instance Attribute Details

#bindingObject

Returns the value of attribute binding.



167
168
169
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 167

def binding
  @binding
end

#logical_joinObject

Returns the value of attribute logical_join.



167
168
169
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 167

def logical_join
  @logical_join
end

#modelObject

Returns the value of attribute model.



167
168
169
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 167

def model
  @model
end

#pathObject

Returns the value of attribute path.



167
168
169
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 167

def path
  @path
end

#reflectionObject

Returns the value of attribute reflection.



167
168
169
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 167

def reflection
  @reflection
end

Instance Method Details

#-@Object Also known as: desc

Negates the condition. Essentially prefixes the condition with NOT in the final query.



289
290
291
292
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 289

def -@
  @negative = !@negative
  self
end

#all(&blk) ⇒ Object

Creates a ConditionGroup that has the logical_join set to “AND”.



253
254
255
256
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 253

def all &blk
  @condition_blocks << ConditionGroup.new(model, "AND", binding, @from, path, &blk)
  @condition_blocks.last
end

#any(&blk) ⇒ Object

Creates a ConditionGroup that has the logical_join set to “OR”.



247
248
249
250
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 247

def any &blk
  @condition_blocks << ConditionGroup.new(model, "OR", binding, @from, path, &blk)
  @condition_blocks.last
end

#assign_joins(join_dependency, ancestries = nil) ⇒ Object

Takes the JoinDependency object and filters it down through the ConditionGroups to make sure each one knows the aliases necessary to refer to each table by its correct name.



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 315

def assign_joins join_dependency, ancestries = nil
  ancestries ||= join_dependency.join_associations.map{|ja| ja.ancestry }
  unless @conditions.empty?
    my_association = unless @path.blank?
                       join_dependency.join_associations[ancestries.index(@path)]
                     else
                       join_dependency.join_base
                     end
    @conditions.each do |column|
      column.assign_join(my_association)
    end
  end
  @condition_blocks.each do |association|
    association.assign_joins(join_dependency, ancestries)
  end
end

#association(name, &blk) ⇒ Object

Similar to #column, this will create an association even if you can’t use the normal method version.



239
240
241
242
243
244
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 239

def association name, &blk
  name = name.to_s.intern
  ref = @model.reflect_on_association(name)
  @condition_blocks << ConditionGroup.new(ref.klass, logical_join, binding, @from, path, ref.name, &blk)
  @condition_blocks.last
end

#column(name) ⇒ Object

Creates a Condition and queues it for inclusion. When calling a method defined during the creation of the ConditionGroup object is the same as calling column(:column_name). This is useful if you need to access a column that happens to coincide with the name of an already-defined method (e.g. anything returned by instance_methods(false) for the given model).



232
233
234
235
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 232

def column name
  @conditions << Condition.new(name)
  @conditions.last
end

#conditionsObject

Returns all the conditions, which is the union of the Conditions and ConditionGroups that belong to this ConditionGroup.



382
383
384
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 382

def conditions
  @conditions + @condition_blocks
end

#fromObject



223
224
225
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 223

def from
  @from
end

#instance(instance_var) ⇒ Object

This is a bit of a hack, due to how Squirrel is built. It can be used to fetch instance variables from the location where the call to #find was made. For example, if called from within your model and you happened to have an instance variable called “@foo”, you can access it by calling instance “@foo” from within your Squirrel query.



397
398
399
400
401
402
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 397

def instance instance_var
  s = eval("self", binding)
  if s
    s.instance_variable_get(instance_var)
  end
end

#limit(lim, off = nil) ⇒ Object

Similar to #paginate, but does not flag the result set for pagination. Takes a limit and an offset (by default the offset is 0).



278
279
280
281
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 278

def limit lim, off = nil
  @limit = ( lim || @limit ).to_i
  @offset = ( off || @offset ).to_i
end

#negative?Boolean

Returns true if this block has been negated using #not, #desc, or #-

Returns:

  • (Boolean)


387
388
389
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 387

def negative?
  @negative
end

#not(&blk) ⇒ Object

Negates the condition. Also works to negate ConditionGroup blocks in a more straightforward manner, like so: any.not do id == 1 name == “Joe” end

# => “NOT( id = 1 OR name = ‘Joe’)”



304
305
306
307
308
309
310
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 304

def not &blk
  @negative = !@negative
  if blk
    @block = blk
    execute_block
  end
end

#order_by(*columns) ⇒ Object

Sets the arguments for the :order parameter. Arguments can be columns (i.e. Conditions) or they can be strings (for “RANDOM()”, etc.). If a Condition is used, and the column is negated using #not or #desc, then the resulting specification in the ORDER clause will be ordered descending. That is, “order_by name.desc” will become “ORDER name DESC”



262
263
264
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 262

def order_by *columns
  @order += [columns].flatten
end

#paginate(opts = {}) ⇒ Object

Flags the result set to be paginated according to the :page and :per_page parameters to this method.



268
269
270
271
272
273
274
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 268

def paginate opts = {}
  @paginator = true
  page = (opts[:page] || 1).to_i
  per_page = (opts[:per_page] || 20).to_i
  page = 1 if page < 1
  limit( per_page, ( page - 1 ) * per_page )
end

#paginate?Boolean

Returns true if this ConditionGroup or any of its subgroups have been flagged for pagination.

Returns:

  • (Boolean)


284
285
286
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 284

def paginate?
  @paginator || @condition_blocks.any?(&:paginate?)
end

#to_find_conditionsObject

Generates the :conditions parameter for this ConditionGroup and all subgroups. It generates them in [“sql”, params] format because of the requirements of LIKE, etc.



360
361
362
363
364
365
366
367
368
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 360

def to_find_conditions
  segments = conditions.collect{|c| c.to_find_conditions }.compact
  return nil if segments.length == 0
  cond = "(" + segments.collect{|s| s.first }.join(" #{logical_join} ") + ")"
  cond = "NOT #{cond}" if negative?
  
  values = segments.inject([]){|all, now| all + now[1..-1] }
  [ cond, *values ]
end

#to_find_includeObject

Generates the parameter for :include for this ConditionGroup and all its subgroups.



333
334
335
336
337
338
339
340
341
342
343
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 333

def to_find_include
  @condition_blocks.inject({}) do |inc, cb|
    if cb.reflection.nil?
      inc.merge_tree(cb.to_find_include)
    else
      inc[cb.reflection] ||= {}
      inc[cb.reflection] = inc[cb.reflection].merge_tree(cb.to_find_include)
      inc
    end
  end
end

#to_find_limitObject

Generates the :limit parameter.



371
372
373
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 371

def to_find_limit
  @limit
end

#to_find_offsetObject

Generates the :offset parameter.



376
377
378
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 376

def to_find_offset
  @offset
end

#to_find_orderObject

Generates the :order parameter for this ConditionGroup. Because this does not reference subgroups it should only be used from the outermost block (which is probably where it makes the most sense to reference it, but it’s worth mentioning)



348
349
350
351
352
353
354
355
356
# File 'lib/legacy_migrations/squirrel/squirrel.rb', line 348

def to_find_order
  if @order.blank?
    nil
  else
    @order.collect do |col|
      col.respond_to?(:full_name) ? (col.full_name + (col.negative? ? " DESC" : "")) : col
    end.join(", ")
  end
end