Class: Lafcadio::Query

Inherits:
Object
  • Object
show all
Defined in:
lib/lafcadio/query.rb

Direct Known Subclasses

Max

Defined Under Namespace

Modules: DomainObjectImpostor Classes: Compare, CompoundCondition, Condition, Equals, In, Include, Inferrer, Like, Max, Not, ObjectFieldImpostor

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(domain_class, opts = {}) ⇒ Query

:nodoc:



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/lafcadio/query.rb', line 174

def initialize(domain_class, opts = {} ) #:nodoc:
	@domain_class, @opts = domain_class, opts
	( @condition, @order_by, @limit ) = [ nil, nil, nil ]
	if ( cond = opts[:condition] )
		@condition = cond
	elsif ( pk_id = opts[:pk_id] )
		@condition = Query::Equals.new( :pk_id, pk_id, domain_class )
	end
	if ( @include = opts[:include] )
		@include = [ @include ] unless @include.is_a?( Array )
	end
	@order_by_order = :asc
end

Instance Attribute Details

#conditionObject (readonly)

Returns the value of attribute condition.



171
172
173
# File 'lib/lafcadio/query.rb', line 171

def condition
  @condition
end

#domain_classObject (readonly)

Returns the value of attribute domain_class.



171
172
173
# File 'lib/lafcadio/query.rb', line 171

def domain_class
  @domain_class
end

#includeObject (readonly)

Returns the value of attribute include.



171
172
173
# File 'lib/lafcadio/query.rb', line 171

def include
  @include
end

#limitObject

Returns the value of attribute limit.



171
172
173
# File 'lib/lafcadio/query.rb', line 171

def limit
  @limit
end

#order_byObject

Returns the value of attribute order_by.



171
172
173
# File 'lib/lafcadio/query.rb', line 171

def order_by
  @order_by
end

#order_by_orderObject

Returns the value of attribute order_by_order.



172
173
174
# File 'lib/lafcadio/query.rb', line 172

def order_by_order
  @order_by_order
end

Class Method Details

.And(*conditions) ⇒ Object

:nodoc:



144
145
146
# File 'lib/lafcadio/query.rb', line 144

def self.And( *conditions ) #:nodoc:
	CompoundCondition.new( *conditions )
end

.infer(*args, &action) ⇒ Object

Infers a query from a block. The first required argument is the domain class. Other optional arguments should be passed in hash form:

:order_by

An array of fields to order the results by.

:order_by_order

Possible values are :asc or :desc. Defaults to :desc.

qry = Query.infer( User ) { |u| u.lname.equals( 'Hwang' ) }
qry.to_sql # => "select * from users where users.lname = 'Hwang'"
qry = Query.infer(
  SKU,
  :order_by => [ :standardPrice, :salePrice ],
  :order_by_order => :desc
) { |s| s.sku.nil? }
qry.to_sql # => "select * from skus where skus.sku is null order by
                 standardPrice, salePrice desc"


161
162
163
164
# File 'lib/lafcadio/query.rb', line 161

def self.infer( *args, &action )
	inferrer = Query::Inferrer.new( *args ) { |obj| action.call( obj ) }
	inferrer.execute
end

.Or(*conditions) ⇒ Object

:nodoc:



166
167
168
169
# File 'lib/lafcadio/query.rb', line 166

def self.Or( *conditions ) #:nodoc:
	conditions << :or
	CompoundCondition.new( *conditions)
end

Instance Method Details

#and(&action) ⇒ Object

Returns a new query representing the condition of the current query and the new inferred query.

qry = Query.infer( User ) { |u| u.lname.equals( 'Hwang' ) }
qry.to_sql # => "select * from users where users.lname = 'Hwang'"
qry = qry.and { |u| u.fname.equals( 'Francis' ) }
qry.to_sql # => "select * from users where (users.lname = 'Hwang' and
                 users.fname = 'Francis')"


195
# File 'lib/lafcadio/query.rb', line 195

def and( &action ); compound( :and, action ); end

#collect(coll) ⇒ Object

:nodoc:



197
198
199
200
201
202
203
# File 'lib/lafcadio/query.rb', line 197

def collect( coll ) #:nodoc:
	if @opts[:group_functions] == [:count]
		[ result_row( [coll.size] ) ]
	else
		raise
	end
end

#compound(comp_type, action) ⇒ Object

:nodoc:



205
206
207
208
209
210
211
212
213
214
# File 'lib/lafcadio/query.rb', line 205

def compound( comp_type, action ) #:nodoc:
	rquery = Query.infer( @domain_class ) { |dobj| action.call( dobj ) }
	q = Query::CompoundCondition.new(
		@condition, rquery.condition, comp_type
	).query
	[ :order_by, :order_by_order, :limit ].each do |attr|
		q.send( attr.to_s + '=', self.send( attr ) )
	end
	q
end

#dobj_satisfies?(dobj) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


216
217
218
# File 'lib/lafcadio/query.rb', line 216

def dobj_satisfies?( dobj ) #:nodoc:
	@condition.nil? or @condition.dobj_satisfies?( dobj )
end

#eql?(other) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


220
221
222
# File 'lib/lafcadio/query.rb', line 220

def eql?( other ) #:nodoc:
	other.is_a?( Query ) && other.to_sql == to_sql
end

#fieldsObject

:nodoc:



224
225
226
# File 'lib/lafcadio/query.rb', line 224

def fields #:nodoc:
	@opts[:group_functions] == [:count] ? 'count(*)' : '*'
end

#hashObject

:nodoc:



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

def hash #:nodoc:
	to_sql.hash
end

#implies?(other_query) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


232
233
234
235
236
237
238
239
240
241
242
# File 'lib/lafcadio/query.rb', line 232

def implies?( other_query ) #:nodoc:
	if other_query == self
		true
	elsif @domain_class == other_query.domain_class
		if other_query.condition.nil? and !self.condition.nil?
			true
		else
			self.condition and self.condition.implies?( other_query.condition )
		end
	end
end

#limit_clause(db) ⇒ Object

:nodoc:



248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/lafcadio/query.rb', line 248

def limit_clause( db ) #:nodoc:
	if @limit
		case db
		when 'Mysql'
			"limit #{ @limit.begin }, #{ @limit.end - @limit.begin + 1 }"
		when 'Pg'
			limit_clause = "limit #{ @limit.end - @limit.begin + 1 }"
			limit_clause += " offset #{ @limit.begin }" if @limit.begin > 0
			limit_clause
		end
	end
end

#one_pk_id?Boolean

Returns:

  • (Boolean)


261
# File 'lib/lafcadio/query.rb', line 261

def one_pk_id?; @condition and @condition.one_pk_id?; end

#or(&action) ⇒ Object

Returns a new query representing the condition of the current query and the new inferred query.

qry = Query.infer( User ) { |u| u.lname.equals( 'Hwang' ) }
qry.to_sql # => "select * from users where users.lname = 'Hwang'"
qry = qry.or { |u| u.fname.equals( 'Francis' ) }
qry.to_sql # => "select * from users where (users.lname = 'Hwang' or
                 users.fname = 'Francis')"


270
# File 'lib/lafcadio/query.rb', line 270

def or( &action ); compound( :or, action ); end

#order_and_limit_collection(objects) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/lafcadio/query.rb', line 287

def order_and_limit_collection( objects )
	objects = objects.sort_by { |dobj|
		if order_by.nil?
			dobj.pk_id
		elsif order_by.is_a?( Array )
			order_by.map { |field_name| dobj.send( field_name ) }
		else
			dobj.send order_by
		end
	}
	objects.reverse! if order_by_order == :desc
	objects = objects[limit] if limit
	objects
end

#order_clauseObject

:nodoc:



276
277
278
279
280
281
282
283
284
285
# File 'lib/lafcadio/query.rb', line 276

def order_clause #:nodoc:
	if @order_by
		field_str = @order_by.map { |f_name|
			@domain_class.field( f_name.to_s ).db_field_name
		}.join( ', ' )
		clause = "order by #{ field_str } "
		clause += @order_by_order == :asc ? 'asc' : 'desc'
		clause
	end
end

#result_row(row) ⇒ Object

:nodoc:



302
303
304
305
306
307
308
# File 'lib/lafcadio/query.rb', line 302

def result_row( row ) #:nodoc:
	if @opts[:group_functions] == [:count]
		{ :count => row.first }
	else
		raise
	end
end

#sql_primary_key_field(domain_class) ⇒ Object

:nodoc:



310
311
312
# File 'lib/lafcadio/query.rb', line 310

def sql_primary_key_field(domain_class) #:nodoc:
	"#{ domain_class.table_name }.#{ domain_class.sql_primary_key_name }"
end

#tablesObject

:nodoc:



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/lafcadio/query.rb', line 314

def tables #:nodoc:
	concrete_classes = domain_class.self_and_concrete_superclasses.reverse
	sql = ''
	dclass = nil
	until concrete_classes.empty?
		prev_dclass = dclass
		dclass = concrete_classes.shift
		if sql == ''
			sql = dclass.table_name
		else
			sql += " inner join #{ dclass.table_name } on #{ sql_primary_key_field( prev_dclass ) } = #{ sql_primary_key_field( dclass ) }"
		end
	end
	if @include
		@include.each do |include_sym|
			field = dclass.field include_sym
			included_dclass = field.linked_type
			sql += " left outer join #{ included_dclass.table_name } on #{ dclass.table_name }.#{ field.db_field_name } = #{ sql_primary_key_field( included_dclass ) }"
		end
	end
	sql
end

#to_sql(db = 'Mysql') ⇒ Object



337
338
339
340
341
342
343
# File 'lib/lafcadio/query.rb', line 337

def to_sql( db = 'Mysql' )
	clauses = [ "select #{ fields }", "from #{ tables }" ]
	clauses << where_clause if where_clause
	clauses << order_clause if order_clause
	clauses << limit_clause( db ) if limit_clause( db )
	clauses.join ' '
end

#where_clauseObject

:nodoc:



345
346
347
348
349
# File 'lib/lafcadio/query.rb', line 345

def where_clause #:nodoc:
	where_clauses = []
	where_clauses << @condition.to_sql if @condition
	!where_clauses.empty? ? 'where ' + where_clauses.join( ' and ' ) : nil
end