Last active
May 11, 2019 18:11
-
-
Save jenya239/ed9b9ca4dd4eb78ab8c749dab0389ad3 to your computer and use it in GitHub Desktop.
sql.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'awesome_print' | |
require 'fileutils' | |
require 'time' | |
class Option | |
attr_reader :name, :underscore, :index | |
attr_accessor :named_value | |
def initialize( name =nil, underscore =false, index =false, to_sql =nil ) | |
@name, @underscore, @index, @to_sql =name, underscore, index, to_sql | |
@named_value =to_sql != nil | |
end | |
def to_sql( value ) #option processing ) | |
if @to_sql | |
sql =instance_exec( value, &@to_sql ) | |
sql =' ' + sql unless @index | |
return sql | |
end | |
value ? " #{ get_sql( value ) }" : '' | |
end | |
protected | |
def get_sql( value ) | |
name =@name .to_s .upcase | |
name .gsub!('_', ' ') unless @underscore | |
name | |
end | |
end | |
class Options | |
attr_reader :hash | |
def initialize | |
arr ={ not_null: [], | |
unique: Proc .new{ |val| "UNIQUE INDEX `#{ val }_UNIQUE` (`#{ val }` ASC)" }, | |
primary: Proc .new{ |val| "PRIMARY KEY (`#{ val }`)" }, | |
auto_increment: [ true ], | |
default: [ false, false, | |
Proc .new{ |val| val =val .is_a?( String ) ? "'#{ val }'" : val .to_s; "DEFAULT #{ val }" } ] } | |
@array =arr .map do |name, val| | |
args =val .is_a?( Array ) ? val : [ false, true, val ] | |
Option .new name, *args | |
end | |
@hash ={} | |
@array .each do |opt| | |
instance_variable_set ( '@' + opt .name .to_s ) .to_sym, opt | |
singleton_class .instance_eval { attr_reader opt .name } | |
@hash[ opt .name ] =opt | |
end | |
@default .named_value =false | |
end | |
def by_name( name ) | |
@hash[ name ] | |
end | |
end | |
class SQL | |
def initialize( sql ) | |
@sql =sql | |
end | |
def to_s | |
@sql | |
end | |
end | |
class Field | |
@@options =Options .new #how without singletons? | |
@@types ={ string: 'VARCHAR( 255 )', text: 'TEXT', int: 'INT', date: 'DATE', | |
timestamp: 'TIMESTAMP' } | |
def initialize( name, type, opts ={} ) | |
@name, @type =name, type | |
defaults ={ not_null: false, unique: false, primary: false, auto_increment: false, default: nil } | |
@opts = defaults .merge( opts ) | |
@opts .each do |name, val| | |
instance_variable_set ( '@' + name .to_s ) .to_sym, val | |
end | |
end | |
def self .type_to_s( type ) | |
@@types[ type ] | |
end | |
def self .type_exists( name ) | |
@@types .key?( name .to_sym ) | |
end | |
def indexes | |
inject [] | |
end | |
def to_s | |
inject "`#{ @name }` #{ self.class.type_to_s( @type ) }" | |
end | |
private | |
def inject( accum ) | |
is_str =accum .is_a?( String ) | |
@opts .each{ |name, value| | |
next if value ==nil | |
opt =@@options .by_name( name ) | |
#print "#{ opt .name } #{ opt .index } #{ is_str ^ opt .index } #{ value }\n" | |
next unless is_str ^ opt .index | |
next if opt .index and not value | |
val =opt .named_value ? @name : value | |
sql =opt .to_sql( val ) | |
is_str ? accum += sql : accum << sql } | |
accum | |
end | |
end | |
class Table | |
def initialize( name, fields =[] ) | |
@name =name .to_s | |
if fields .is_a? String | |
args =self .class .parse( fields ) | |
@fields =args .map{ |arr| Field .new *arr } | |
else | |
@fields =fields | |
end | |
end | |
def to_s | |
s ="CREATE TABLE IF NOT EXISTS `#{ @name }` (\n\t" | |
s +=( @fields .map( &:to_s ) + @fields .inject([]){|arr,f| arr + f .indexes } ) .join( ",\n\t" ) | |
s +="\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n" | |
end | |
def add_field( name, type, opts ={} ) | |
@fields << Field .new( name, type, opts ) | |
end | |
def self .parse_value( str ) | |
if ( str[ 0 ] =="'" and str[ -1 ] =="'" ) or ( str[ 0 ] =='"' and str[ -1 ] =='"' ) | |
str[ 1..-2 ] | |
elsif str =~ /^\d+$/ | |
str .to_i | |
elsif str =~ /^\d*\.\d+$/ | |
str .to_f | |
else | |
SQL .new str | |
end | |
end | |
def self .parse_line( line ) | |
tokens =line .strip .split | |
is_type = Field .type_exists( tokens .first ) | |
if is_type | |
type =tokens .shift .to_sym | |
tokens .map{ |t| [ t, type ] } | |
else | |
name =tokens .shift | |
type =tokens .shift .to_sym | |
opts ={} | |
prev =nil | |
while tokens .size > 0 | |
token =tokens .shift | |
if token =='=' | |
opts[ prev .to_sym ] =parse_value tokens .shift | |
else | |
opts[ token .to_sym ] =true | |
end | |
prev =token | |
end | |
#opts =tokens .inject( {} ){ |h, s| h[ s .to_sym ] =true; h } | |
[ [ name, type, opts ] ] | |
#p name, type, opts | |
end | |
end | |
def self .parse( s ) | |
strings =s .strip .split "\n" | |
strings .inject( [] ){ |arr, line| arr += parse_line line } | |
end | |
end | |
t =Table .new 'posts', """ | |
id int not_null unique primary auto_increment | |
post_id int | |
created_at timestamp default = CURRENT_TIMESTAMP | |
string title author image | |
text content | |
""" | |
print t .to_s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment