slick 0.16.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -8
  3. data/.slick +0 -0
  4. data/.travis.yml +3 -5
  5. data/LICENSE +20 -0
  6. data/README.md +3 -32
  7. data/Rakefile +1 -0
  8. data/config.rb +8 -0
  9. data/config.ru +4 -0
  10. data/exe/slick +8 -0
  11. data/lib/slick.rb +50 -3
  12. data/lib/slick/command.rb +29 -0
  13. data/lib/slick/commands/create_database.rb +9 -0
  14. data/lib/slick/commands/drop_database.rb +9 -0
  15. data/lib/slick/commands/init_database.rb +9 -0
  16. data/lib/slick/commands/list_commands.rb +14 -0
  17. data/lib/slick/commands/migrate_database.rb +9 -0
  18. data/lib/slick/commands/reset_database.rb +10 -0
  19. data/lib/slick/commands/start_console.rb +14 -0
  20. data/lib/slick/commands/start_server.rb +14 -0
  21. data/lib/slick/concern.rb +19 -0
  22. data/lib/slick/database.rb +168 -0
  23. data/lib/slick/database/abstract_row.rb +13 -0
  24. data/lib/slick/database/column.rb +64 -0
  25. data/lib/slick/database/migration.rb +36 -0
  26. data/lib/slick/database/migrator.rb +67 -0
  27. data/lib/slick/database/row.rb +127 -0
  28. data/lib/slick/database/table.rb +381 -0
  29. data/lib/slick/database/union.rb +86 -0
  30. data/lib/slick/helper.rb +24 -0
  31. data/lib/slick/helpers.rb +4 -0
  32. data/lib/slick/helpers/dir.rb +6 -0
  33. data/lib/slick/helpers/echo.rb +6 -0
  34. data/lib/slick/helpers/grab.rb +10 -0
  35. data/lib/slick/helpers/html_builder.rb +180 -0
  36. data/lib/slick/helpers/text_builder.rb +22 -0
  37. data/lib/slick/monkey_patches/html_escape.rb +8 -0
  38. data/lib/slick/monkey_patches/html_unescape.rb +8 -0
  39. data/lib/slick/monkey_patches/paramify.rb +11 -0
  40. data/lib/slick/monkey_patches/pluralize.rb +8 -0
  41. data/lib/slick/monkey_patches/require_all.rb +18 -0
  42. data/lib/slick/monkey_patches/singularize.rb +8 -0
  43. data/lib/slick/monkey_patches/url_encode.rb +8 -0
  44. data/lib/slick/monkey_patches/url_escape.rb +8 -0
  45. data/lib/slick/monkey_patches/url_unescape.rb +8 -0
  46. data/lib/slick/project.rb +53 -0
  47. data/lib/slick/project_watcher.rb +36 -0
  48. data/lib/slick/registry.rb +39 -0
  49. data/lib/slick/request.rb +6 -0
  50. data/lib/slick/resource_factories/config.rb +14 -0
  51. data/lib/slick/resource_factories/database.rb +6 -0
  52. data/lib/slick/resource_factories/database_schema_cache.rb +6 -0
  53. data/lib/slick/resource_factories/database_session_cache.rb +6 -0
  54. data/lib/slick/resource_factories/env.rb +14 -0
  55. data/lib/slick/resource_factories/environment.rb +6 -0
  56. data/lib/slick/resource_factories/file.rb +6 -0
  57. data/lib/slick/resource_factories/params.rb +6 -0
  58. data/lib/slick/resource_factories/project.rb +6 -0
  59. data/lib/slick/resource_factories/request.rb +6 -0
  60. data/lib/slick/resource_factories/response.rb +6 -0
  61. data/lib/slick/resource_factories/web.rb +11 -0
  62. data/lib/slick/resource_factory.rb +35 -0
  63. data/lib/slick/resource_provider.rb +51 -0
  64. data/lib/slick/response.rb +14 -0
  65. data/lib/slick/util.rb +72 -0
  66. data/lib/slick/util/inflections.rb +122 -0
  67. data/lib/slick/util/params_hash.rb +38 -0
  68. data/lib/slick/version.rb +1 -1
  69. data/lib/slick/web.rb +4 -0
  70. data/lib/slick/web/dir.rb +92 -0
  71. data/lib/slick/web/file.rb +66 -0
  72. data/lib/slick/web/file_interpreter.rb +24 -0
  73. data/lib/slick/web/file_interpreters/erb.rb +33 -0
  74. data/lib/slick/web/file_interpreters/js.rb +17 -0
  75. data/lib/slick/web/file_interpreters/rb.rb +14 -0
  76. data/lib/slick/web/file_interpreters/sass.rb +30 -0
  77. data/lib/slick/web/node.rb +35 -0
  78. data/lib/slick/workspace.rb +8 -0
  79. data/slick.gemspec +15 -4
  80. data/web/_layout.erb +13 -0
  81. data/web/index.erb +7 -0
  82. data/web/javascripts/index.js +2 -0
  83. data/web/stylesheets/index.scss +2 -0
  84. metadata +127 -11
  85. data/Gemfile.lock +0 -22
  86. data/bin/console +0 -14
  87. data/bin/setup +0 -8
@@ -0,0 +1,13 @@
1
+
2
+ require "slick/concern"
3
+
4
+ module Slick::Database::AbstractRow
5
+
6
+ include Slick::Concern
7
+
8
+ def register(name)
9
+ method_missing(:register, name, :abstract => true)
10
+ Slick::Database::Union.register(name.pluralize)
11
+ end
12
+
13
+ end
@@ -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