slick 0.16.3 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -8
- data/.slick +0 -0
- data/.travis.yml +3 -5
- data/LICENSE +20 -0
- data/README.md +3 -32
- data/Rakefile +1 -0
- data/config.rb +8 -0
- data/config.ru +4 -0
- data/exe/slick +8 -0
- data/lib/slick.rb +50 -3
- data/lib/slick/command.rb +29 -0
- data/lib/slick/commands/create_database.rb +9 -0
- data/lib/slick/commands/drop_database.rb +9 -0
- data/lib/slick/commands/init_database.rb +9 -0
- data/lib/slick/commands/list_commands.rb +14 -0
- data/lib/slick/commands/migrate_database.rb +9 -0
- data/lib/slick/commands/reset_database.rb +10 -0
- data/lib/slick/commands/start_console.rb +14 -0
- data/lib/slick/commands/start_server.rb +14 -0
- data/lib/slick/concern.rb +19 -0
- data/lib/slick/database.rb +168 -0
- data/lib/slick/database/abstract_row.rb +13 -0
- data/lib/slick/database/column.rb +64 -0
- data/lib/slick/database/migration.rb +36 -0
- data/lib/slick/database/migrator.rb +67 -0
- data/lib/slick/database/row.rb +127 -0
- data/lib/slick/database/table.rb +381 -0
- data/lib/slick/database/union.rb +86 -0
- data/lib/slick/helper.rb +24 -0
- data/lib/slick/helpers.rb +4 -0
- data/lib/slick/helpers/dir.rb +6 -0
- data/lib/slick/helpers/echo.rb +6 -0
- data/lib/slick/helpers/grab.rb +10 -0
- data/lib/slick/helpers/html_builder.rb +180 -0
- data/lib/slick/helpers/text_builder.rb +22 -0
- data/lib/slick/monkey_patches/html_escape.rb +8 -0
- data/lib/slick/monkey_patches/html_unescape.rb +8 -0
- data/lib/slick/monkey_patches/paramify.rb +11 -0
- data/lib/slick/monkey_patches/pluralize.rb +8 -0
- data/lib/slick/monkey_patches/require_all.rb +18 -0
- data/lib/slick/monkey_patches/singularize.rb +8 -0
- data/lib/slick/monkey_patches/url_encode.rb +8 -0
- data/lib/slick/monkey_patches/url_escape.rb +8 -0
- data/lib/slick/monkey_patches/url_unescape.rb +8 -0
- data/lib/slick/project.rb +53 -0
- data/lib/slick/project_watcher.rb +36 -0
- data/lib/slick/registry.rb +39 -0
- data/lib/slick/request.rb +6 -0
- data/lib/slick/resource_factories/config.rb +14 -0
- data/lib/slick/resource_factories/database.rb +6 -0
- data/lib/slick/resource_factories/database_schema_cache.rb +6 -0
- data/lib/slick/resource_factories/database_session_cache.rb +6 -0
- data/lib/slick/resource_factories/env.rb +14 -0
- data/lib/slick/resource_factories/environment.rb +6 -0
- data/lib/slick/resource_factories/file.rb +6 -0
- data/lib/slick/resource_factories/params.rb +6 -0
- data/lib/slick/resource_factories/project.rb +6 -0
- data/lib/slick/resource_factories/request.rb +6 -0
- data/lib/slick/resource_factories/response.rb +6 -0
- data/lib/slick/resource_factories/web.rb +11 -0
- data/lib/slick/resource_factory.rb +35 -0
- data/lib/slick/resource_provider.rb +51 -0
- data/lib/slick/response.rb +14 -0
- data/lib/slick/util.rb +72 -0
- data/lib/slick/util/inflections.rb +122 -0
- data/lib/slick/util/params_hash.rb +38 -0
- data/lib/slick/version.rb +1 -1
- data/lib/slick/web.rb +4 -0
- data/lib/slick/web/dir.rb +92 -0
- data/lib/slick/web/file.rb +66 -0
- data/lib/slick/web/file_interpreter.rb +24 -0
- data/lib/slick/web/file_interpreters/erb.rb +33 -0
- data/lib/slick/web/file_interpreters/js.rb +17 -0
- data/lib/slick/web/file_interpreters/rb.rb +14 -0
- data/lib/slick/web/file_interpreters/sass.rb +30 -0
- data/lib/slick/web/node.rb +35 -0
- data/lib/slick/workspace.rb +8 -0
- data/slick.gemspec +15 -4
- data/web/_layout.erb +13 -0
- data/web/index.erb +7 -0
- data/web/javascripts/index.js +2 -0
- data/web/stylesheets/index.scss +2 -0
- metadata +127 -11
- data/Gemfile.lock +0 -22
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
class Slick::Database::Column
|
3
|
+
|
4
|
+
TYPE_TO_MYSQL_COLUMN_TYPE_MAP = {
|
5
|
+
"primary_key" => "int(11) unsigned",
|
6
|
+
"foreign_key" => "int(11) unsigned",
|
7
|
+
"binary" => "longblob",
|
8
|
+
"boolean" => "enum('false','true')",
|
9
|
+
"date" => "date",
|
10
|
+
"datetime" => "datetime",
|
11
|
+
"decimal" => "decimal",
|
12
|
+
"float" => "float",
|
13
|
+
"integer" => "int(11)",
|
14
|
+
"string" => "varchar(255)",
|
15
|
+
"text" => "longtext",
|
16
|
+
"time" => "time",
|
17
|
+
"timestamp" => "datetime"
|
18
|
+
}
|
19
|
+
|
20
|
+
MYSQL_COLUMN_TYPE_TO_TYPE_MAP = TYPE_TO_MYSQL_COLUMN_TYPE_MAP.invert
|
21
|
+
|
22
|
+
def initialize(table, name, type = nil)
|
23
|
+
@table = table
|
24
|
+
@name = name
|
25
|
+
@type = type
|
26
|
+
end
|
27
|
+
|
28
|
+
def type
|
29
|
+
@type ||= @table.send(@name).instance_variable_get(:@type)
|
30
|
+
end
|
31
|
+
|
32
|
+
def exist?
|
33
|
+
!type.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def create(type = 'string', options = {})
|
37
|
+
type = type.to_s
|
38
|
+
options = {:index => type == 'foreign_key'}.paramify.merge(options)
|
39
|
+
database = @table.instance_variable_get(:@database)
|
40
|
+
|
41
|
+
@table.create
|
42
|
+
|
43
|
+
database.run(
|
44
|
+
'alter table ?', @table.class.name.to_sym,
|
45
|
+
'add column ?', @name.to_sym, TYPE_TO_MYSQL_COLUMN_TYPE_MAP[type]
|
46
|
+
) if !exist?
|
47
|
+
|
48
|
+
database.run(
|
49
|
+
'alter table ?', @table.class.name.to_sym,
|
50
|
+
'add index(?)', @name.to_sym
|
51
|
+
) if options['index'] == 'true'
|
52
|
+
end
|
53
|
+
|
54
|
+
def drop
|
55
|
+
database = @table.instance_variable_get(:@database)
|
56
|
+
|
57
|
+
database.run(
|
58
|
+
'alter table ?', @table.class.name.to_sym,
|
59
|
+
'drop ?', @name.to_sym
|
60
|
+
)
|
61
|
+
@type = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
require "slick/registry"
|
3
|
+
require "slick/helpers"
|
4
|
+
|
5
|
+
class Slick::Database::Migration
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
include Slick::Registry
|
10
|
+
|
11
|
+
def register(name, *args)
|
12
|
+
throw "Invalid migration name '#{name}' - it must begin with a unix timestamp" if !name.to_s.match(/\A\d+/)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def schema_version
|
17
|
+
if matches = name.to_s.match(/\A(\d+)/)
|
18
|
+
matches[0].to_i
|
19
|
+
else
|
20
|
+
0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
include Slick::Helpers
|
27
|
+
|
28
|
+
def up
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def down
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
class Slick::Database::Migrator
|
3
|
+
|
4
|
+
def initialize(database)
|
5
|
+
@database = database
|
6
|
+
|
7
|
+
if !database.slick_applied_migrations.exist?
|
8
|
+
database.slick_applied_migrations.add_column(:schema_version, :integer)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def current_schema_version
|
13
|
+
slick_applied_migration = @database.slick_applied_migrations.order_by(:schema_version, :desc).first(:cache => "schema")
|
14
|
+
if slick_applied_migration
|
15
|
+
slick_applied_migration.schema_version
|
16
|
+
else
|
17
|
+
-1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def latest_schema_version
|
22
|
+
migrations.length > 0 ? migrations.last.schema_version : -1
|
23
|
+
end
|
24
|
+
|
25
|
+
def migrate(to_schema_version = latest_schema_version)
|
26
|
+
if to_schema_version > current_schema_version
|
27
|
+
migrate_up(to_schema_version)
|
28
|
+
elsif to_schema_version < current_schema_version
|
29
|
+
migrate_down(to_schema_version)
|
30
|
+
else
|
31
|
+
puts "Migrations up-to-date"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def migrate_up(to_schema_version)
|
38
|
+
migrations.each do |migration|
|
39
|
+
if migration.schema_version <= to_schema_version && !migration_applied?(migration)
|
40
|
+
puts "Up: #{migration.name}"
|
41
|
+
migration.new.up
|
42
|
+
@database.slick_applied_migrations.insert(:schema_version => migration.schema_version)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def migrate_down(to_schema_version)
|
48
|
+
migrations.reverse.each do |migration|
|
49
|
+
if migration.schema_version > to_schema_version && migration_applied?(migration)
|
50
|
+
puts "Down: #{migration.name}"
|
51
|
+
migration.new.down
|
52
|
+
@database.slick_applied_migrations.schema_version_eq(migration.schema_version).delete
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def migrations
|
58
|
+
@migrations ||= Slick::Database::Migration.registered_classes.values.sort do |a, b|
|
59
|
+
a.schema_version <=> b.schema_version
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def migration_applied?(migration)
|
64
|
+
@database.slick_applied_migrations.schema_version_eq(migration.schema_version).count > 0
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
|
2
|
+
class Slick::Database::Row
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
include Slick::Registry
|
7
|
+
|
8
|
+
attr_accessor :table_class
|
9
|
+
|
10
|
+
def register(name, options = {})
|
11
|
+
if options[:abstract]
|
12
|
+
union_class = Slick::Database::Union.for(name.pluralize, :register_if_not_exists => true)
|
13
|
+
union_class.table_classes << table_class if !union_class.table_classes.include?(table_class)
|
14
|
+
else
|
15
|
+
super(name)
|
16
|
+
@table_class = Slick::Database::Table.for(name.pluralize, :register_if_not_exists => true)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_many(name, *args, &block)
|
21
|
+
table_class.has_many(name, *args, &block)
|
22
|
+
class_eval("def #{name}; self.class.table_class.new(@database).id_eq(id).#{name}; end")
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_one(name, *args, &block)
|
26
|
+
table_class.has_one(name, *args, &block)
|
27
|
+
class_eval("def #{name}; self.class.table_class.new(@database).id_eq(id).#{name}.first; end")
|
28
|
+
end
|
29
|
+
|
30
|
+
def belongs_to(name, *args, &block)
|
31
|
+
table_class.belongs_to(name, *args, &block)
|
32
|
+
class_eval("def #{name}; self.class.table_class.new(@database).id_eq(id).#{name}.first; end")
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(database, fields = {})
|
38
|
+
@database = database
|
39
|
+
@fields = fields
|
40
|
+
@altered_fields = {}
|
41
|
+
@update_level = 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
@fields
|
46
|
+
end
|
47
|
+
|
48
|
+
def id=(value)
|
49
|
+
raise "Id fields can't be set directly on a row"
|
50
|
+
end
|
51
|
+
|
52
|
+
def update(fields = {}, &block)
|
53
|
+
@update_level += 1
|
54
|
+
fields.each{ |name, value| send("#{name}=", value) }
|
55
|
+
|
56
|
+
if block
|
57
|
+
if block.arity > 0
|
58
|
+
block.call(self)
|
59
|
+
else
|
60
|
+
instance_eval(&block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
@update_level -=1
|
64
|
+
|
65
|
+
if @update_level == 0 && @altered_fields.count > 0
|
66
|
+
if @fields['id'].nil?
|
67
|
+
@database.run(_generate_insert_sql())
|
68
|
+
else
|
69
|
+
@database.run(_generate_update_sql())
|
70
|
+
end
|
71
|
+
end
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete
|
76
|
+
@database.run('delete from ? where id = ?', self.class.name.pluralize.to_sym, id)
|
77
|
+
end
|
78
|
+
|
79
|
+
def method_missing(name, *args, &block)
|
80
|
+
if args.length == 1 && matches = name.match(/\A(.+)=\z/)
|
81
|
+
if _column_names.include?(matches[1])
|
82
|
+
@altered_fields[matches[1]] = args.first if @fields[matches[1]] != args.first
|
83
|
+
update if @update_level == 0
|
84
|
+
else
|
85
|
+
raise "No such column '#{matches[1]} exists for table '#{self.class.name.pluralize}'"
|
86
|
+
end
|
87
|
+
elsif args.length == 0
|
88
|
+
name = name.to_s
|
89
|
+
if _column_names.include?(name)
|
90
|
+
if @altered_fields.key?(name)
|
91
|
+
@altered_fields[name]
|
92
|
+
else
|
93
|
+
@fields[name]
|
94
|
+
end
|
95
|
+
else
|
96
|
+
raise "No such column '#{name}' exists for table '#{self.class.name.pluralize}'"
|
97
|
+
end
|
98
|
+
else
|
99
|
+
super
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def _generate_insert_sql
|
104
|
+
out = []
|
105
|
+
out << ['insert into ?', self.class.name.pluralize.to_sym]
|
106
|
+
out << ["(#{@altered_fields.keys.map{'?'}.join(', ')})", @altered_fields.keys.map{|name| name.to_sym}]
|
107
|
+
out << ["values(#{@altered_fields.keys.map{'?'}.join(', ')})", @altered_fields.values]
|
108
|
+
out
|
109
|
+
end
|
110
|
+
|
111
|
+
def _generate_update_sql
|
112
|
+
out = []
|
113
|
+
out << ['update ? ', self.class.name.pluralize.to_sym]
|
114
|
+
out << ['set']
|
115
|
+
out << @altered_fields.keys.map{'? = ?'}.join(', ')
|
116
|
+
@altered_fields.each do |name, value|
|
117
|
+
out << [name.to_sym, value]
|
118
|
+
end
|
119
|
+
out << ['where id = ?', @fields['id']]
|
120
|
+
out
|
121
|
+
end
|
122
|
+
|
123
|
+
def _column_names
|
124
|
+
@_column_names ||= Slick::Database::Table.create(self.class.name.pluralize, @database).columns.keys
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,381 @@
|
|
1
|
+
|
2
|
+
require "slick/registry"
|
3
|
+
require "slick/helper"
|
4
|
+
|
5
|
+
class Slick::Database::Table
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
include Slick::Registry
|
10
|
+
|
11
|
+
def register(name, *args)
|
12
|
+
super
|
13
|
+
Class.new(Slick::Helper){ register name }.define_method "call" do |*args, &block|
|
14
|
+
database.send(self.class.name, *args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def relationships
|
19
|
+
@relationships ||= {}.paramify
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_many(name, options = {})
|
23
|
+
relationships[name] = {
|
24
|
+
"name" => name.to_s,
|
25
|
+
"collection_name" => name.to_s,
|
26
|
+
"from_key" => "id",
|
27
|
+
"to_key" => "#{self.name.singularize}_id",
|
28
|
+
"cascade_delete" => "true"
|
29
|
+
}
|
30
|
+
relationships[name].merge!(options)
|
31
|
+
class_eval("def #{name}; _join('#{name}'); end")
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_one(name, options = {})
|
35
|
+
relationships[name] = {
|
36
|
+
"name" => name.to_s,
|
37
|
+
"collection_name" => name.pluralize,
|
38
|
+
"from_key" => "id",
|
39
|
+
"to_key" => "#{self.name.singularize}_id",
|
40
|
+
"cascade_delete" => "true"
|
41
|
+
}
|
42
|
+
relationships[name].merge!(options)
|
43
|
+
class_eval("def #{name}; _join('#{name}'); end")
|
44
|
+
end
|
45
|
+
|
46
|
+
def belongs_to(name, options = {})
|
47
|
+
relationships[name] = {
|
48
|
+
"name" => name.to_s,
|
49
|
+
"collection_name" => name.pluralize,
|
50
|
+
"from_key" => "#{name}_id",
|
51
|
+
"to_key" => "id",
|
52
|
+
"cascade_delete" => "false"
|
53
|
+
}
|
54
|
+
relationships[name].merge!(options)
|
55
|
+
class_eval("def #{name}; _join('#{name}'); end")
|
56
|
+
end
|
57
|
+
|
58
|
+
def scope(name, &block)
|
59
|
+
#call block with join parents as arguments
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def initialize(database, join_parent = nil)
|
65
|
+
@database = database
|
66
|
+
@join_parent = join_parent
|
67
|
+
@alias_counters = {} if !@join_parent
|
68
|
+
@alias = _generate_alias(self.class.name)
|
69
|
+
|
70
|
+
if !@join_parent
|
71
|
+
@from_sql = ['? as ?', self.class.name.to_sym, @alias.to_sym]
|
72
|
+
@where_sql = []
|
73
|
+
@order_by_sql = []
|
74
|
+
@limit_sql = []
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def +(collection)
|
79
|
+
Slick::Database::Union.new(@database, [clone, collection])
|
80
|
+
end
|
81
|
+
|
82
|
+
def clone
|
83
|
+
out = super
|
84
|
+
out.instance_eval do
|
85
|
+
instance_variables.each do |name|
|
86
|
+
if name.match(/\A@_/)
|
87
|
+
remove_instance_variable(name)
|
88
|
+
elsif name != :@database
|
89
|
+
instance_variable_set(name, instance_variable_get(name).clone)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
out
|
94
|
+
end
|
95
|
+
|
96
|
+
def back(count = 1)
|
97
|
+
out = self
|
98
|
+
count.times{ out = out.instance_variable_get(:@join_parent) }
|
99
|
+
out
|
100
|
+
end
|
101
|
+
|
102
|
+
def where(*predicate, &block)
|
103
|
+
if block
|
104
|
+
join_path = []
|
105
|
+
current = self
|
106
|
+
block.arity.times do
|
107
|
+
join_path << current
|
108
|
+
current = current.instance_eval{ @join_parent } if current
|
109
|
+
end
|
110
|
+
predicate << block.call(*join_path)
|
111
|
+
end
|
112
|
+
out = clone
|
113
|
+
where_sql = out.send(:_join_root).instance_variable_get(:@where_sql)
|
114
|
+
if where_sql.count > 0
|
115
|
+
where_sql << ['and', predicate]
|
116
|
+
else
|
117
|
+
where_sql << predicate
|
118
|
+
end
|
119
|
+
out
|
120
|
+
end
|
121
|
+
|
122
|
+
def order_by(column, direction = 'asc')
|
123
|
+
out = clone
|
124
|
+
column = out.send(column.to_s) if !column.kind_of?(Slick::Database::Column)
|
125
|
+
order_by_sql = out.send(:_join_root).instance_variable_get(:@order_by_sql)
|
126
|
+
if order_by_sql.count > 0
|
127
|
+
order_by_sql << [", ? #{direction.to_s == 'desc' ? 'desc' : 'asc'}", column]
|
128
|
+
else
|
129
|
+
order_by_sql << ["? #{direction.to_s == 'desc' ? 'desc' : 'asc'}", column]
|
130
|
+
end
|
131
|
+
out
|
132
|
+
end
|
133
|
+
|
134
|
+
def clear_order_by
|
135
|
+
out = clone
|
136
|
+
out.send(:_join_root).instance_variable_set(:@order_by_sql, [])
|
137
|
+
out
|
138
|
+
end
|
139
|
+
|
140
|
+
def paginate(page = 1, page_size = 10)
|
141
|
+
out = clone
|
142
|
+
out.send(:_join_root).instance_variable_set(:@limit_sql, ['?, ?', (page - 1) * page_size, page_size])
|
143
|
+
out
|
144
|
+
end
|
145
|
+
|
146
|
+
def clear_pagination
|
147
|
+
out = clone
|
148
|
+
out.send(:_join_root).instance_variable_set(:@limit_sql, [])
|
149
|
+
out
|
150
|
+
end
|
151
|
+
|
152
|
+
def all(options = {})
|
153
|
+
@database.run(_generate_select_sql(options), options)
|
154
|
+
end
|
155
|
+
|
156
|
+
def each(options = {}, &block)
|
157
|
+
@database.run(_generate_select_sql(options), options).each(&block)
|
158
|
+
end
|
159
|
+
|
160
|
+
def first(options = {})
|
161
|
+
paginate(1, 1).all(options).pop
|
162
|
+
end
|
163
|
+
|
164
|
+
def [](id)
|
165
|
+
id_eq(id).first
|
166
|
+
end
|
167
|
+
|
168
|
+
def count(options = {})
|
169
|
+
first(options.paramify.merge('select' => 'count(*)')).values.pop
|
170
|
+
end
|
171
|
+
|
172
|
+
def explain(options = {})
|
173
|
+
@database.send(:_prepare, _generate_select_sql(options))
|
174
|
+
end
|
175
|
+
|
176
|
+
def inspect
|
177
|
+
all
|
178
|
+
end
|
179
|
+
|
180
|
+
def insert(fields = {}, &block)
|
181
|
+
Slick::Database::Row.create(self.class.name.singularize, @database).update(fields, &block)
|
182
|
+
end
|
183
|
+
|
184
|
+
def update(fields = {}, &block)
|
185
|
+
each{|row| row.update(fields, &block)}
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
def delete
|
190
|
+
each{|row| row.delete}
|
191
|
+
self
|
192
|
+
end
|
193
|
+
|
194
|
+
def columns
|
195
|
+
if exist?
|
196
|
+
out = {}
|
197
|
+
@database.run('describe ?', self, :cache => 'schema').each do |row|
|
198
|
+
name = row['Field']
|
199
|
+
if name == 'id'
|
200
|
+
type = 'primary_key'
|
201
|
+
else
|
202
|
+
type = Slick::Database::Column::MYSQL_COLUMN_TYPE_TO_TYPE_MAP[
|
203
|
+
row['Type']
|
204
|
+
] || "string"
|
205
|
+
end
|
206
|
+
out[name] = Slick::Database::Column.new(self, name, type)
|
207
|
+
end
|
208
|
+
out
|
209
|
+
else
|
210
|
+
{}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def exist?
|
215
|
+
@database.tables.keys.include?(self.class.name)
|
216
|
+
end
|
217
|
+
|
218
|
+
def create
|
219
|
+
@database.create()
|
220
|
+
@database.run(
|
221
|
+
"create table ?", self.class.name.to_sym, "(",
|
222
|
+
"id int(11) unsigned auto_increment primary key",
|
223
|
+
")"
|
224
|
+
) if !exist?
|
225
|
+
end
|
226
|
+
|
227
|
+
def drop
|
228
|
+
@database.run("drop table ?", self.class.name.to_sym) if exist?
|
229
|
+
end
|
230
|
+
|
231
|
+
def add_column(name, *args)
|
232
|
+
send(name).create(*args)
|
233
|
+
end
|
234
|
+
|
235
|
+
def remove_column(name)
|
236
|
+
send(name).drop
|
237
|
+
end
|
238
|
+
|
239
|
+
COMPARISON_OPERATORS = {
|
240
|
+
'eq' => '? = ?',
|
241
|
+
'ne' => '? != ?',
|
242
|
+
'lt' => '? < ?',
|
243
|
+
'gt' => '',
|
244
|
+
'le' => '? <= ?',
|
245
|
+
'ge' => '? >= ?',
|
246
|
+
'begins_with' => '? like concat(?, '%')',
|
247
|
+
'ends_with' => '? like concat('%', ?)',
|
248
|
+
'contains' => '? like concat('%', ?, '%')'
|
249
|
+
}
|
250
|
+
|
251
|
+
COMPARISON_OPERATOR_METHOD_PATTERN = Regexp.new("\\A(.+)_(#{COMPARISON_OPERATORS.keys.join('|')})\\z")
|
252
|
+
|
253
|
+
def method_missing(name, *args, &block)
|
254
|
+
if args.length == 0 && block.nil?
|
255
|
+
columns[name.to_s] || Slick::Database::Column.new(self, name)
|
256
|
+
elsif args.length == 1 && block.nil? && matches = name.to_s.match(COMPARISON_OPERATOR_METHOD_PATTERN)
|
257
|
+
if send(matches[1]).exist?
|
258
|
+
where(COMPARISON_OPERATORS[matches[2]], send(matches[1]), *args)
|
259
|
+
else
|
260
|
+
where('1 = 2')
|
261
|
+
end
|
262
|
+
else
|
263
|
+
super
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
private
|
268
|
+
|
269
|
+
def _join(relationship_name)
|
270
|
+
relationship = self.class.relationships[relationship_name]
|
271
|
+
if Slick::Database::Union.registered_classes[relationship.collection_name]
|
272
|
+
_join_to_union(relationship)
|
273
|
+
else
|
274
|
+
_join_to_table(relationship)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def _join_to_union(relationship)
|
279
|
+
union_class = Slick::Database::Union.registered_classes[relationship.collection_name]
|
280
|
+
tables = union_class.table_classes.map do |table_class|
|
281
|
+
join_parent = clone
|
282
|
+
out = table_class.new(@database, join_parent)
|
283
|
+
out.send(:_join_root).instance_eval do
|
284
|
+
@from_sql << [', ? as ?', out.class.name.to_sym, out.instance_variable_get(:@alias).to_sym]
|
285
|
+
@where_sql << ["#{@where_sql.count > 0 ? 'and ' : ''}? = ?", join_parent.send(relationship.from_key), out.send(relationship.to_key)]
|
286
|
+
if matches = relationship.from_key.match(/\A(.+)_id\z/)
|
287
|
+
@where_sql << ['and ? = ?', join_parent.send("#{matches[1]}_type"), out.class.name.singularize]
|
288
|
+
elsif matches = relationship.to_key.match(/\A(.+)_id\z/)
|
289
|
+
@where_sql << ['and ? = ?', out.send("#{matches[1]}_type"), join_parent.class.name.singularize]
|
290
|
+
end
|
291
|
+
end
|
292
|
+
out
|
293
|
+
end
|
294
|
+
union_class.new(@database, tables)
|
295
|
+
end
|
296
|
+
|
297
|
+
def _join_to_table(relationship)
|
298
|
+
join_parent = clone
|
299
|
+
out = Slick::Database::Table.create(relationship.collection_name, @database, join_parent)
|
300
|
+
out.send(:_join_root).instance_eval do
|
301
|
+
@from_sql << [', ? as ?', out.class.name.to_sym, out.instance_variable_get(:@alias).to_sym]
|
302
|
+
@where_sql << ["#{@where_sql.count > 0 ? 'and ' : ''}? = ?", join_parent.send(relationship.from_key), out.send(relationship.to_key)]
|
303
|
+
end
|
304
|
+
out
|
305
|
+
end
|
306
|
+
|
307
|
+
def _generate_select_sql(options = {})
|
308
|
+
options = {"column_names" => columns.keys}.paramify.merge(options)
|
309
|
+
|
310
|
+
out = ['select']
|
311
|
+
|
312
|
+
if options.key?('select')
|
313
|
+
out << options['select']
|
314
|
+
else
|
315
|
+
options.column_names.values.each do |column_name|
|
316
|
+
column = send(column_name)
|
317
|
+
if column.exist?
|
318
|
+
out << ['? as ?, ', column, column_name.to_sym]
|
319
|
+
else
|
320
|
+
out << ['null as ?, ', column_name.to_sym]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
out << ['? as ?', self.class.name.singularize, :_type]
|
324
|
+
end
|
325
|
+
|
326
|
+
if options.key?('from')
|
327
|
+
from_sql = options.from || []
|
328
|
+
else
|
329
|
+
from_sql = _join_root.instance_variable_get(:@from_sql)
|
330
|
+
end
|
331
|
+
out << ["from", from_sql] if from_sql.count > 0
|
332
|
+
|
333
|
+
if options.key?('where')
|
334
|
+
where_sql = options.where || []
|
335
|
+
else
|
336
|
+
where_sql = _join_root.instance_variable_get(:@where_sql)
|
337
|
+
end
|
338
|
+
out << ['where', where_sql] if where_sql.count > 0
|
339
|
+
|
340
|
+
if options.key?('order_by')
|
341
|
+
order_by_sql = options.order_by || []
|
342
|
+
else
|
343
|
+
order_by_sql = _join_root.instance_variable_get(:@order_by_sql)
|
344
|
+
end
|
345
|
+
out << ['order by', order_by_sql] if order_by_sql.count > 0
|
346
|
+
|
347
|
+
if options.key?('limit')
|
348
|
+
limit_sql = options.limit || []
|
349
|
+
else
|
350
|
+
limit_sql = _join_root.instance_variable_get(:@limit_sql)
|
351
|
+
end
|
352
|
+
out << ['limit', limit_sql] if limit_sql.count > 0
|
353
|
+
|
354
|
+
out
|
355
|
+
end
|
356
|
+
|
357
|
+
def _generate_alias(name)
|
358
|
+
alias_counters = _join_root.instance_variable_get(:@alias_counters)
|
359
|
+
|
360
|
+
name = name.to_s
|
361
|
+
alias_counters[name] = 0 if alias_counters[name].nil?
|
362
|
+
alias_counters[name] += 1
|
363
|
+
|
364
|
+
if alias_counters[name] == 1
|
365
|
+
name
|
366
|
+
else
|
367
|
+
"#{name}#{alias_counters[name]}"
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def _join_root
|
372
|
+
@_join_root ||= begin
|
373
|
+
out = self
|
374
|
+
while join_parent = out.instance_variable_get(:@join_parent)
|
375
|
+
out = join_parent
|
376
|
+
end
|
377
|
+
out
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|