Class: SqlMunger::FieldSet

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Quoter
Defined in:
lib/sql_munger/field_set.rb

Overview

This is a class encapsulating a set of fields and providing various methods for producing SQL strings from them.

You can also call operators on fieldset (+ - | & ), or with an array as the right-hand parameter.

Fields can be either plain strings, or objects responding to name, sql_type, null, default. If they’re plain strings, they’re converted to FieldSet::Field objects, which have a name attribute. Mixing different classes in the same FieldSet won’t work.

Defined Under Namespace

Classes: Field

Instance Attribute Summary collapse

Attributes included from Quoter

#quoter

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Quoter

#identifier_quoter, included, #value_quoter

Constructor Details

#initialize(collection_or_enum, options = {}, &block) ⇒ FieldSet

collection_or_enum is a collection of field names.

If block is supplied, collection_or_enum is a collection of objects that when map( &block ) is applied returns a collection of field names.

options is intended to easily pass a :qualifier and a :quoter.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/sql_munger/field_set.rb', line 41

def initialize( collection_or_enum, options = {}, &block )
  @fields = 
  if collection_or_enum.all?{|x| x.is_a?( String ) || x.is_a?( Symbol ) }
    collection_or_enum.to_a.map{|x| Field.new(x.to_s)}
  else
    first = collection_or_enum.first
    first.name rescue raise( "Can't use default method :name on #{first}" )
    collection_or_enum.to_a.dup
  end
  
  @field_name_block = block || lambda {|x| x.name}
  
  # set options
  options.each do |key,value|
    send( "#{key}=", value )
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object

passes - + & | to operate, otherwise calls method_missing



159
160
161
162
163
164
165
166
# File 'lib/sql_munger/field_set.rb', line 159

def method_missing( meth, *args, &block )
  @operations ||= %w{ - + & | }.map{|x| x.to_sym}
  if @operations.include?( meth )
    operate( meth, args.first )
  else
    super
  end
end

Instance Attribute Details

#fieldsObject

the field objects, which may be objects or Strings. See field_names if you definitely always want either Strings or Symbols



92
93
94
# File 'lib/sql_munger/field_set.rb', line 92

def fields
  @fields
end

#qualifierObject

whatever is in front of the fields, usually a table name It’s stored as an array of parts, to be quoted and joined with ‘.’ when generating SQL



171
172
173
# File 'lib/sql_munger/field_set.rb', line 171

def qualifier
  @qualifier
end

Class Method Details

.[](fieldset_or_array) ⇒ Object

Create from either a Array of field names (NOTE not field objects) or from another FieldSet



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

def self.[]( fieldset_or_array )
  case fieldset_or_array
    when Array
      check_strings( fieldset_or_array, "Use #{self.class.name}.new" )
      FieldSet.new( fieldset_or_array )
    
    when FieldSet
      field_set = fieldset_or_array
      field_set.dup
    
    when NilClass
      nil
    
    else
      raise "dunno what to do with #{fieldset_or_array.inspect}"
  end
end

.check_strings(ary, msg = nil) ⇒ Object

raise an exception with msg appended if any element of ary is not a String. Otherwise return true



61
62
63
64
65
66
67
68
# File 'lib/sql_munger/field_set.rb', line 61

def self.check_strings( ary, msg = nil )
  ary.each do |elt|
    unless elt.is_a?( String ) || elt.is_a?( Symbol )
      raise [ "#{elt.inspect} is neither String nor Symbol", msg ].compact.join(' ')
    end
  end
  true
end

Instance Method Details

#==(other) ⇒ Object



340
341
342
343
344
345
# File 'lib/sql_munger/field_set.rb', line 340

def ==( other )
  return false unless other.is_a?( self.class )
  qualifier == other.qualifier and
  fields == other.fields and
  @field_name_block == other.instance_variable_get( '@field_name_block' )
end

#comparison(arg, operator = '=') ⇒ Object

return an SQL set of comparison expressions, joined with ‘and’ suitable for a where clause



304
305
306
307
308
309
310
311
# File 'lib/sql_munger/field_set.rb', line 304

def comparison( arg, operator = '=' )
  if arg.is_a?( String ) && qualifier.nil?
    raise "no local qualifier"
  end
  
  st = express( arg, 0, :operator => operator, :joiner => ' and ' )
  "( #{st} )"
end

#definitions(options = {:joiner => ', '}) ⇒ Object

probably only works with column definitions from ActiveRecord



325
326
327
328
329
330
# File 'lib/sql_munger/field_set.rb', line 325

def definitions( options = {:joiner => ', '} )
  fields.map do |field|
    raise "sql_type has no value" if field.sql_type.nil? || field.sql_type == ''
    "#{field.name} #{field.sql_type}"
  end.join( options[:joiner] )
end

#each(&block) ⇒ Object

enumerate through field_names



115
116
117
# File 'lib/sql_munger/field_set.rb', line 115

def each( &block )
  field_names.each( &block )
end

#empty?Boolean

Returns:

  • (Boolean)


336
337
338
# File 'lib/sql_munger/field_set.rb', line 336

def empty?
  fields.empty?
end

#express(arg, level = 0, options = { :operator => '=', :joiner => ',' }) ⇒ Object

return a string of the fields with some operator and value can be used for where clauses and update statements. arg is either a String, which is treated passed to TableName.new or a TableName, which qualifies the rhs or an Array (actually something with a zip method) which is used as a set of values or a Hash (or something with to_hash) from which the values corresponding to the fields in this object are extracted, and used to compare with



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/sql_munger/field_set.rb', line 261

def express( arg, level = 0, options = { :operator => '=', :joiner => ',' } )
  expressions = 
  case
    when arg.is_a?( String )
      express( TableName.new( arg ), level+1, options )
    
    when arg.is_a?( TableName )
      other_table = arg
      field_names.map do |field|
        "#{qualify( field )} #{options[:operator]} #{qualify( other_table, field )}"
      end
    
    when arg.is_a?( Hash )
      field_names.map do |field|
        "#{qualify( field )} #{options[:operator]} #{value_quoter.quote( arg[field] )}"
      end
    
    when arg.respond_to?( :to_hash )
      express( arg.to_hash, level+1, options )
    
    when arg.respond_to?( :zip )
      raise "#{arg.inspect} doesn't match number of fields in #{field_names}" unless size == arg.size
      field_names.zip( [ *arg ] ).map do |field,value|
        "#{qualify( field )} #{options[:operator]} #{value_quoter.quote( value )}"
      end
    
    else
      raise "Don't know what to do with #{arg.inspect}"
  end
  
  if level == 0
    if options[:joiner].nil?
      expressions
    else
      expressions.join( options[:joiner] )
    end
  else
    expressions
  end
end

#field_namesObject

the names of the fields



110
111
112
# File 'lib/sql_munger/field_set.rb', line 110

def field_names
  @field_names ||= sanity_check_fields
end

#find_field(name) ⇒ Object

return the field object for name, or nil if it doesn’t exist



121
122
123
# File 'lib/sql_munger/field_set.rb', line 121

def find_field( name )
  fields.find{|x| name == @field_name_block.call(x) }
end

#hash_values(hash_values, &block) ⇒ Object

return a hash of field names to their corresponding transformed values from hash_values. block will be used to transform each value.



222
223
224
225
226
227
228
229
230
231
232
# File 'lib/sql_munger/field_set.rb', line 222

def hash_values( hash_values, &block )
  inject({}) do |hash,field|
    hash[field] =
    if block.nil?
      hash_values[field]
    else
      block.call( hash_values[field] )
    end
    hash
  end
end

#list(qualifier = nil) ⇒ Object

return the set of field names, separated by ‘, ’



194
195
196
197
198
# File 'lib/sql_munger/field_set.rb', line 194

def list( qualifier = nil )
  field_names.map do |field|
    qualify( qualifier, field )
  end.join(', ')
end

#operate(operation, other) ⇒ Object

+ - & | (append, difference, intersection union) same as array. other can be another FieldSet, or an Array Called from method_missing



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
# File 'lib/sql_munger/field_set.rb', line 128

def operate( operation, other )
  case other
    when FieldSet
      # fetch new fields using the operated set of field_names
      new_field_objects = field_names.send( operation, other.field_names ).map do |name|
        # first try self's fields, then try other's fields
        find_field( name ) || other.find_field( name )
      end
      
      # create the new FieldSet
      FieldSet.new( new_field_objects, :quoter => quoter, :qualifier => qualifier )
    
    when Array
      case other.first
        when String
          operate( operation, FieldSet.new( other ) )
        
        when Symbol
          operate( operation, FieldSet.new( other ) )
          
        else
          # assume it's a field object
          FieldSet.new( fields.send( operation, other ), :quoter => quoter, :qualifier => qualifier )
      end
    
    else
      raise "don't know what to do with #{other.inspect}"
  end
end

#qualified_listObject Also known as: qlist

prepend the qualifier to each field name



201
202
203
# File 'lib/sql_munger/field_set.rb', line 201

def qualified_list
  list( qualifier )
end

#qualify(*args) ⇒ Object

one arg is a field, qualified by qualifier many args is qualifier, field



209
210
211
212
213
214
215
216
217
218
# File 'lib/sql_munger/field_set.rb', line 209

def qualify( *args )
  case args.flatten.size
    when 1
      [ qualifier, args.first ]
    
    else
      args
    
  end.flatten.compact.map{|x| identifier_quoter.quote_ident(x)}.join('.')
end

#quoted_values(hash_values) ⇒ Object

return a comma-separated list of the quoted values for this fieldset



236
237
238
239
240
# File 'lib/sql_munger/field_set.rb', line 236

def quoted_values( hash_values )
  map do |field|
    value_quoter.quote( hash_values[field] )
  end
end

#reconstruct(&block) ⇒ Object

Return a new instance of FieldSet. The result of fields is passed to block, and the result of that is passed to FieldSet.new. Useful for doing set operations that aren’t covered by + - & |



105
106
107
# File 'lib/sql_munger/field_set.rb', line 105

def reconstruct( &block )
  FieldSet.new( block.call( fields ) )
end

#sizeObject



332
333
334
# File 'lib/sql_munger/field_set.rb', line 332

def size
  fields.size
end

#update(arg) ⇒ Object

return something suitable for an update statement



314
315
316
317
318
319
320
321
322
# File 'lib/sql_munger/field_set.rb', line 314

def update( arg )
  save_qualifier = qualifier
  begin
    self.qualifier = nil
    express( arg, 0, :operator => '=', :joiner => ', ' )
  ensure
    self.qualifier = save_qualifier
  end
end

#values(hash_values, &block) ⇒ Object

extract the values for these fields from hash_values block will be applied to each value



244
245
246
247
248
249
250
251
252
# File 'lib/sql_munger/field_set.rb', line 244

def values( hash_values, &block )
  map do |field|
    if block.nil?
      hash_values[field]
    else
      block.call( hash_values[field] )
    end
  end
end