Permalink
Browse files

WIP for bin/squeal and target SQL safety changes

  • Loading branch information...
1 parent 3109420 commit a88fc3cf9298a28b15c37c2944ec81ecacd28d42 @bernerdschaefer bernerdschaefer committed May 20, 2010
Showing with 150 additions and 4 deletions.
  1. +125 −0 bin/squeal
  2. +25 −4 lib/squealer/target.rb
View
@@ -0,0 +1,125 @@
+#!/usr/bin/env ruby
+
+model = ARGV[0]
+unless model
+ $stderr.puts "usage: squeal ModelName"
+ exit 1
+end
+
+require 'config/boot'
+require 'config/environment'
+
+model = Object.const_get(model)
+
+fields = model.fields.values.sort_by(&:name)
+associations = model.associations
+
+# SQL #
+def create_table(model, parent = nil)
+ fields = model.fields.values.sort_by(&:name)
+ columns = []
+ columns << "`id` CHAR(24) PRIMARY KEY"
+ columns << "`#{parent.name.underscore}_id` CHAR(24)" if parent
+
+ fields.each do |field|
+ mysql_type = case field.type.name
+ when "Boolean"
+ "BOOLEAN"
+ when "Time"
+ "TIMESTAMP"
+ when "Date"
+ "DATE"
+ when "Float"
+ "FLOAT(10,2)"
+ when "Integer"
+ "INT"
+ else
+ "TEXT"
+ end
+ columns << "`#{field.name[0..63]}` #{mysql_type}"
+ end
+
+ table_name = if parent
+ "#{parent.name.underscore}_#{model.name.underscore}"
+ else
+ "#{model.name.underscore}"
+ end
+
+ "DROP TABLE IF EXISTS `#{table_name}`; CREATE TABLE `#{table_name}` (#{columns.join(", ")});"
+end
+
+# SQUEAL #
+
+def create_squeal(model, indent=false, parents = [])
+ fields = model.fields.values.sort_by(&:name)
+
+ parent = parents.last
+ table_name = if parent
+ "#{parent.name.underscore}_#{model.name.underscore}"
+ else
+ "#{model.name.underscore}"
+ end
+
+ squeal = if parent
+ "#{parent.name.underscore}.#{model.name.tableize}.each do |#{model.name.underscore}|\n" \
+ " #{table_name} = #{model.name.underscore}"
+ else
+ "import.source(\"#{model.name.tableize}\").each do |#{model.name.underscore}|"
+ end
+
+ schemas = [create_table(model, parent)]
+
+ squeal << <<-EOS
+
+ target(:#{table_name}) do
+ EOS
+ if parent
+ squeal << <<-EOS
+ assign(:#{parent.name.underscore}_id)
+ EOS
+ end
+
+ fields.each do |field|
+ field_name = field.name
+ case
+ when %w(type target).include?(field.name)
+ value = " { #{table_name}['#{field.name}'] }"
+ when field.name.size > 64
+ field_name = field.name[0..63]
+ value = " { #{table_name}.#{field.name} }"
+ when field.name =~ /(.*)_id$/
+ value = " { #{table_name}.#{$1} }"
+ end
+ squeal << <<-EOS
+ assign(:#{field_name})#{value}
+ EOS
+ end
+
+ model.associations.values.each do |association|
+ begin
+ if [Mongoid::Associations::HasMany, Mongoid::Associations::HasOne].include?(association.association)
+ unless parents.include?(association.klass)
+ ruby, sql = create_squeal(association.klass, true, parents | [model])
+ squeal << "\n" + ruby + "\n"
+ schemas |= sql
+ end
+ end
+ rescue NameError
+ end
+ end
+
+ squeal << <<-EOS
+ end
+end
+ EOS
+ squeal.gsub!(/^/, " ") if indent
+ return squeal, schemas
+end
+
+squeal, schema = create_squeal(model)
+
+if ARGV[1] == "sql"
+ puts schema.join("\n")
+else
+ puts squeal
+end
View
@@ -103,8 +103,10 @@ def targets
def execute_sql(sql)
statement = Database.instance.export.prepare(sql)
- values = [*column_values] + [*column_values] #array expando
- statement.send(:execute, @row_id, *values) #expand values into distinct arguments
+
+ statement.send(:execute, @row_id, *(typecast_values * 2)) #expand values into distinct arguments
+ rescue Mysql::Error, TypeError
+ raise "Failed to execute statement: #{sql} with #{values.inspect}. Raised: #{$!.to_s}"
end
def pk_name
@@ -113,7 +115,7 @@ def pk_name
def column_names
return if @column_names.size == 0
- ",#{@column_names.join(',')}"
+ ",#{@column_names.map { |name| quote_identifier(name) }.join(',')}"
end
def column_values
@@ -130,10 +132,29 @@ def column_value_markers
def column_markers
return if @column_names.size == 0
result = ""
- @column_names.each {|k| result << "#{k}=?," }
+ @column_names.each {|k| result << "#{quote_identifier(k)}=?," }
result.chop
end
+ def typecast_values
+ column_values.map do |value|
+ case value
+ when true, false
+ value.to_i
+ when Symbol
+ value.to_s
+ when Array
+ value.join(",")
+ else
+ value
+ end
+ end
+ end
+
+ def quote_identifier(name)
+ "`#{name}`"
+ end
+
class Queue < DelegateClass(Array)
include Singleton

0 comments on commit a88fc3c

Please sign in to comment.