torque-postgresql 3.0.1 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +17 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +61 -7
- data/lib/torque/postgresql/adapter/schema_creation.rb +3 -9
- data/lib/torque/postgresql/adapter/schema_dumper.rb +39 -7
- data/lib/torque/postgresql/adapter/schema_statements.rb +40 -0
- data/lib/torque/postgresql/adapter.rb +2 -2
- data/lib/torque/postgresql/associations/preloader/loader_query.rb +1 -1
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +149 -0
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +74 -22
- data/lib/torque/postgresql/auxiliary_statement.rb +39 -40
- data/lib/torque/postgresql/base.rb +29 -25
- data/lib/torque/postgresql/config.rb +17 -0
- data/lib/torque/postgresql/inheritance.rb +3 -1
- data/lib/torque/postgresql/migration/command_recorder.rb +8 -8
- data/lib/torque/postgresql/railtie.rb +5 -1
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +28 -15
- data/lib/torque/postgresql/schema_cache.rb +6 -1
- data/lib/torque/postgresql/table_name.rb +41 -0
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +2 -1
- data/spec/models/category.rb +2 -0
- data/spec/models/internal/user.rb +5 -0
- data/spec/schema.rb +16 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/tests/auxiliary_statement_spec.rb +374 -35
- data/spec/tests/enum_set_spec.rb +7 -6
- data/spec/tests/schema_spec.rb +92 -0
- data/spec/tests/table_inheritance_spec.rb +11 -15
- metadata +17 -5
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'auxiliary_statement/settings'
|
4
|
+
require_relative 'auxiliary_statement/recursive'
|
4
5
|
|
5
6
|
module Torque
|
6
7
|
module PostgreSQL
|
@@ -8,17 +9,20 @@ module Torque
|
|
8
9
|
TABLE_COLUMN_AS_STRING = /\A(?:"?(\w+)"?\.)?"?(\w+)"?\z/.freeze
|
9
10
|
|
10
11
|
class << self
|
11
|
-
attr_reader :config
|
12
|
+
attr_reader :config, :table_name
|
12
13
|
|
13
14
|
# Find or create the class that will handle statement
|
14
15
|
def lookup(name, base)
|
15
16
|
const = name.to_s.camelize << '_' << self.name.demodulize
|
16
17
|
return base.const_get(const, false) if base.const_defined?(const, false)
|
17
|
-
|
18
|
+
|
19
|
+
base.const_set(const, Class.new(self)).tap do |klass|
|
20
|
+
klass.instance_variable_set(:@table_name, name.to_s)
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
# Create a new instance of an auxiliary statement
|
21
|
-
def instantiate(statement, base, options
|
25
|
+
def instantiate(statement, base, **options)
|
22
26
|
klass = while base < ActiveRecord::Base
|
23
27
|
list = base.auxiliary_statements_list
|
24
28
|
break list[statement] if list.present? && list.key?(statement)
|
@@ -26,15 +30,15 @@ module Torque
|
|
26
30
|
base = base.superclass
|
27
31
|
end
|
28
32
|
|
29
|
-
return klass.new(options) unless klass.nil?
|
33
|
+
return klass.new(**options) unless klass.nil?
|
30
34
|
raise ArgumentError, <<-MSG.squish
|
31
35
|
There's no '#{statement}' auxiliary statement defined for #{base.class.name}.
|
32
36
|
MSG
|
33
37
|
end
|
34
38
|
|
35
39
|
# Fast access to statement build
|
36
|
-
def build(statement, base,
|
37
|
-
klass = instantiate(statement, base, options)
|
40
|
+
def build(statement, base, bound_attributes = [], join_sources = [], **options)
|
41
|
+
klass = instantiate(statement, base, **options)
|
38
42
|
result = klass.build(base)
|
39
43
|
|
40
44
|
bound_attributes.concat(klass.bound_attributes)
|
@@ -56,7 +60,7 @@ module Torque
|
|
56
60
|
# A way to create auxiliary statements outside of models configurations,
|
57
61
|
# being able to use on extensions
|
58
62
|
def create(table_or_settings, &block)
|
59
|
-
klass = Class.new(
|
63
|
+
klass = Class.new(self)
|
60
64
|
|
61
65
|
if block_given?
|
62
66
|
klass.instance_variable_set(:@table_name, table_or_settings)
|
@@ -89,7 +93,8 @@ module Torque
|
|
89
93
|
def configure(base, instance)
|
90
94
|
return @config unless @config.respond_to?(:call)
|
91
95
|
|
92
|
-
|
96
|
+
recursive = self < AuxiliaryStatement::Recursive
|
97
|
+
settings = Settings.new(base, instance, recursive)
|
93
98
|
settings.instance_exec(settings, &@config)
|
94
99
|
settings
|
95
100
|
end
|
@@ -98,11 +103,6 @@ module Torque
|
|
98
103
|
def table
|
99
104
|
@table ||= ::Arel::Table.new(table_name)
|
100
105
|
end
|
101
|
-
|
102
|
-
# Get the name of the table of the configurated statement
|
103
|
-
def table_name
|
104
|
-
@table_name ||= self.name.demodulize.split('_').first.underscore
|
105
|
-
end
|
106
106
|
end
|
107
107
|
|
108
108
|
delegate :config, :table, :table_name, :relation, :configure, :relation_query?,
|
@@ -111,15 +111,14 @@ module Torque
|
|
111
111
|
attr_reader :bound_attributes, :join_sources
|
112
112
|
|
113
113
|
# Start a new auxiliary statement giving extra options
|
114
|
-
def initialize(
|
115
|
-
options = args.extract_options!
|
114
|
+
def initialize(*, **options)
|
116
115
|
args_key = Torque::PostgreSQL.config.auxiliary_statement.send_arguments_key
|
117
116
|
|
118
117
|
@join = options.fetch(:join, {})
|
119
118
|
@args = options.fetch(args_key, {})
|
120
119
|
@where = options.fetch(:where, {})
|
121
120
|
@select = options.fetch(:select, {})
|
122
|
-
@join_type = options
|
121
|
+
@join_type = options[:join_type]
|
123
122
|
|
124
123
|
@bound_attributes = []
|
125
124
|
@join_sources = []
|
@@ -131,7 +130,7 @@ module Torque
|
|
131
130
|
@join_sources.clear
|
132
131
|
|
133
132
|
# Prepare all the data for the statement
|
134
|
-
prepare(base)
|
133
|
+
prepare(base, configure(base, self))
|
135
134
|
|
136
135
|
# Add the join condition to the list
|
137
136
|
@join_sources << build_join(base)
|
@@ -141,9 +140,9 @@ module Torque
|
|
141
140
|
end
|
142
141
|
|
143
142
|
private
|
143
|
+
|
144
144
|
# Setup the statement using the class configuration
|
145
|
-
def prepare(base)
|
146
|
-
settings = configure(base, self)
|
145
|
+
def prepare(base, settings)
|
147
146
|
requires = Array.wrap(settings.requires).flatten.compact
|
148
147
|
@dependencies = ensure_dependencies(requires, base).flatten.compact
|
149
148
|
|
@@ -151,14 +150,12 @@ module Torque
|
|
151
150
|
@query = settings.query
|
152
151
|
|
153
152
|
# Call a proc to get the real query
|
154
|
-
if @query.
|
153
|
+
if @query.respond_to?(:call)
|
155
154
|
call_args = @query.try(:arity) === 0 ? [] : [OpenStruct.new(@args)]
|
156
155
|
@query = @query.call(*call_args)
|
157
|
-
@args = []
|
158
156
|
end
|
159
157
|
|
160
|
-
#
|
161
|
-
@query_table = settings.query_table unless relation_query?(@query)
|
158
|
+
# Merge select attributes provided on the instance creation
|
162
159
|
@select = settings.attributes.merge(@select) if settings.attributes.present?
|
163
160
|
|
164
161
|
# Merge join settings
|
@@ -168,7 +165,7 @@ module Torque
|
|
168
165
|
@association = settings.through.to_s
|
169
166
|
elsif relation_query?(@query)
|
170
167
|
@association = base.reflections.find do |name, reflection|
|
171
|
-
break name if @query.klass.eql?
|
168
|
+
break name if @query.klass.eql?(reflection.klass)
|
172
169
|
end
|
173
170
|
end
|
174
171
|
end
|
@@ -234,15 +231,6 @@ module Torque
|
|
234
231
|
as a query object on #{self.class.name}.
|
235
232
|
MSG
|
236
233
|
|
237
|
-
# Expose join columns
|
238
|
-
if relation_query?(@query)
|
239
|
-
query_table = @query.arel_table
|
240
|
-
conditions.children.each do |item|
|
241
|
-
@query.select_values += [query_table[item.left.name]] \
|
242
|
-
if item.left.relation.eql?(table)
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
234
|
# Build the join based on the join type
|
247
235
|
arel_join.new(table, table.create_on(conditions))
|
248
236
|
end
|
@@ -263,21 +251,31 @@ module Torque
|
|
263
251
|
|
264
252
|
# Mount the list of selected attributes
|
265
253
|
def expose_columns(base, query_table = nil)
|
254
|
+
# Add the columns necessary for the join
|
255
|
+
list = @join_sources.each_with_object(@select) do |join, hash|
|
256
|
+
join.right.expr.children.each do |item|
|
257
|
+
hash[item.left.name] = nil if item.left.relation.eql?(table)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
266
261
|
# Add select columns to the query and get exposed columns
|
267
|
-
|
268
|
-
base.select_extra_values += [table[right.to_s]]
|
269
|
-
|
262
|
+
list.filter_map do |left, right|
|
263
|
+
base.select_extra_values += [table[right.to_s]] unless right.nil?
|
264
|
+
next unless query_table
|
265
|
+
|
266
|
+
col = project(left, query_table)
|
267
|
+
right.nil? ? col : col.as(right.to_s)
|
270
268
|
end
|
271
269
|
end
|
272
270
|
|
273
271
|
# Ensure that all the dependencies are loaded in the base relation
|
274
272
|
def ensure_dependencies(list, base)
|
275
273
|
with_options = list.extract_options!.to_a
|
276
|
-
(list + with_options).map do |
|
277
|
-
dependent_klass = base.model.auxiliary_statements_list[
|
274
|
+
(list + with_options).map do |name, options|
|
275
|
+
dependent_klass = base.model.auxiliary_statements_list[name]
|
278
276
|
|
279
277
|
raise ArgumentError, <<-MSG.squish if dependent_klass.nil?
|
280
|
-
The '#{
|
278
|
+
The '#{name}' auxiliary statement dependency can't found on
|
281
279
|
#{self.class.name}.
|
282
280
|
MSG
|
283
281
|
|
@@ -285,7 +283,8 @@ module Torque
|
|
285
283
|
cte.is_a?(dependent_klass)
|
286
284
|
end
|
287
285
|
|
288
|
-
|
286
|
+
options ||= {}
|
287
|
+
AuxiliaryStatement.build(name, base, bound_attributes, join_sources, **options)
|
289
288
|
end
|
290
289
|
end
|
291
290
|
|
@@ -5,15 +5,27 @@ module Torque
|
|
5
5
|
module Base
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
+
##
|
9
|
+
# :singleton-method: schema
|
10
|
+
# :call-seq: schema
|
11
|
+
#
|
12
|
+
# The schema to which the table belongs to.
|
13
|
+
|
8
14
|
included do
|
9
15
|
mattr_accessor :belongs_to_many_required_by_default, instance_accessor: false
|
16
|
+
class_attribute :schema, instance_writer: false
|
10
17
|
end
|
11
18
|
|
12
19
|
module ClassMethods
|
13
20
|
delegate :distinct_on, :with, :itself_only, :cast_records, to: :all
|
14
21
|
|
15
|
-
#
|
16
|
-
|
22
|
+
# Make sure that table name is an instance of TableName class
|
23
|
+
def reset_table_name
|
24
|
+
self.table_name = TableName.new(self, super)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Whenever the base model is inherited, add a list of auxiliary
|
28
|
+
# statements like the one that loads inherited records' relname
|
17
29
|
def inherited(subclass)
|
18
30
|
super
|
19
31
|
|
@@ -22,32 +34,14 @@ module Torque
|
|
22
34
|
|
23
35
|
record_class = ActiveRecord::Relation._record_class_attribute
|
24
36
|
|
25
|
-
# Define helper methods to return the class of the given records
|
26
|
-
subclass.auxiliary_statement record_class do |cte|
|
27
|
-
pg_class = ::Arel::Table.new('pg_class')
|
28
|
-
arel_query = ::Arel::SelectManager.new(pg_class)
|
29
|
-
arel_query.project(pg_class['oid'], pg_class['relname'].as(record_class.to_s))
|
30
|
-
|
31
|
-
cte.query 'pg_class', arel_query.to_sql
|
32
|
-
cte.attributes col(record_class) => record_class
|
33
|
-
cte.join tableoid: :oid
|
34
|
-
end
|
35
|
-
|
36
37
|
# Define the dynamic attribute that returns the same information as
|
37
38
|
# the one provided by the auxiliary statement
|
38
39
|
subclass.dynamic_attribute(record_class) do
|
39
|
-
|
40
|
+
klass = self.class
|
41
|
+
next klass.table_name unless klass.physically_inheritances?
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
quoted_id = ::Arel::Nodes::Quoted.new(id)
|
44
|
-
|
45
|
-
query = ::Arel::SelectManager.new(pg_class)
|
46
|
-
query.join(source).on(pg_class['oid'].eq(source['tableoid']))
|
47
|
-
query.where(source[subclass.primary_key].eq(quoted_id))
|
48
|
-
query.project(pg_class['relname'])
|
49
|
-
|
50
|
-
self.class.connection.select_value(query)
|
43
|
+
query = klass.unscoped.where(subclass.primary_key => id)
|
44
|
+
query.pluck(klass.arel_table['tableoid'].cast('regclass')).first
|
51
45
|
end
|
52
46
|
end
|
53
47
|
|
@@ -258,7 +252,7 @@ module Torque
|
|
258
252
|
# attributes key:
|
259
253
|
# Provides a map of attributes to be exposed to the main query.
|
260
254
|
#
|
261
|
-
# For
|
255
|
+
# For instance, if the statement query has an 'id' column that you
|
262
256
|
# want it to be accessed on the main query as 'item_id',
|
263
257
|
# you can use:
|
264
258
|
# attributes id: :item_id, 'MAX(id)' => :max_id,
|
@@ -299,6 +293,16 @@ module Torque
|
|
299
293
|
klass.configurator(block)
|
300
294
|
end
|
301
295
|
alias cte auxiliary_statement
|
296
|
+
|
297
|
+
# Creates a new recursive auxiliary statement (CTE) under the base
|
298
|
+
# Very similar to the regular auxiliary statement, but with two-part
|
299
|
+
# query where one is executed first and the second recursively
|
300
|
+
def recursive_auxiliary_statement(table, &block)
|
301
|
+
klass = AuxiliaryStatement::Recursive.lookup(table, self)
|
302
|
+
auxiliary_statements_list[table.to_sym] = klass
|
303
|
+
klass.configurator(block)
|
304
|
+
end
|
305
|
+
alias recursive_cte recursive_auxiliary_statement
|
302
306
|
end
|
303
307
|
end
|
304
308
|
|
@@ -39,6 +39,19 @@ module Torque
|
|
39
39
|
|
40
40
|
end
|
41
41
|
|
42
|
+
# Configure multiple schemas
|
43
|
+
config.nested(:schemas) do |schemas|
|
44
|
+
|
45
|
+
# Defines a list of LIKE-based schemas to not consider for a multiple
|
46
|
+
# schema database
|
47
|
+
schemas.blacklist = %w[information_schema pg_%]
|
48
|
+
|
49
|
+
# Defines a list of LIKE-based schemas to consider for a multiple schema
|
50
|
+
# database
|
51
|
+
schemas.whitelist = %w[public]
|
52
|
+
|
53
|
+
end
|
54
|
+
|
42
55
|
# Configure auxiliary statement features
|
43
56
|
config.nested(:auxiliary_statement) do |cte|
|
44
57
|
|
@@ -50,6 +63,10 @@ module Torque
|
|
50
63
|
# auxiliary statement in order to perform detached CTEs
|
51
64
|
cte.exposed_class = 'TorqueCTE'
|
52
65
|
|
66
|
+
# Estipulate a class name (which may contain namespace) that expose the
|
67
|
+
# recursive auxiliary statement in order to perform detached CTEs
|
68
|
+
cte.exposed_recursive_class = 'TorqueRecursiveCTE'
|
69
|
+
|
53
70
|
end
|
54
71
|
|
55
72
|
# Configure ENUM features
|
@@ -55,7 +55,9 @@ module Torque
|
|
55
55
|
|
56
56
|
# Check if the model's table depends on any inheritance
|
57
57
|
def physically_inherited?
|
58
|
-
@physically_inherited
|
58
|
+
return @physically_inherited if defined?(@physically_inherited)
|
59
|
+
|
60
|
+
@physically_inherited = connection.schema_cache.dependencies(
|
59
61
|
defined?(@table_name) ? @table_name : decorated_table_name,
|
60
62
|
).present?
|
61
63
|
rescue ActiveRecord::ConnectionNotEstablished
|
@@ -5,24 +5,24 @@ module Torque
|
|
5
5
|
module Migration
|
6
6
|
module CommandRecorder
|
7
7
|
|
8
|
-
# Records the rename operation for types
|
8
|
+
# Records the rename operation for types
|
9
9
|
def rename_type(*args, &block)
|
10
10
|
record(:rename_type, args, &block)
|
11
11
|
end
|
12
12
|
|
13
|
-
# Inverts the type
|
13
|
+
# Inverts the type rename operation
|
14
14
|
def invert_rename_type(args)
|
15
15
|
[:rename_type, args.reverse]
|
16
16
|
end
|
17
17
|
|
18
|
-
# Records the creation of
|
19
|
-
def
|
20
|
-
record(:
|
18
|
+
# Records the creation of a schema
|
19
|
+
def create_schema(*args, &block)
|
20
|
+
record(:create_schema, args, &block)
|
21
21
|
end
|
22
22
|
|
23
|
-
# Inverts the creation of
|
24
|
-
def
|
25
|
-
[:
|
23
|
+
# Inverts the creation of a schema
|
24
|
+
def invert_create_schema(args)
|
25
|
+
[:drop_schema, [args.first]]
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
@@ -30,11 +30,15 @@ module Torque
|
|
30
30
|
Torque::PostgreSQL::Attributes::Enum.lookup(name).sample
|
31
31
|
end
|
32
32
|
|
33
|
-
# Define the exposed constant for auxiliary statements
|
33
|
+
# Define the exposed constant for both types of auxiliary statements
|
34
34
|
if torque_config.auxiliary_statement.exposed_class.present?
|
35
35
|
*ns, name = torque_config.auxiliary_statement.exposed_class.split('::')
|
36
36
|
base = ns.present? ? Object.const_get(ns.join('::')) : Object
|
37
37
|
base.const_set(name, Torque::PostgreSQL::AuxiliaryStatement)
|
38
|
+
|
39
|
+
*ns, name = torque_config.auxiliary_statement.exposed_recursive_class.split('::')
|
40
|
+
base = ns.present? ? Object.const_get(ns.join('::')) : Object
|
41
|
+
base.const_set(name, Torque::PostgreSQL::AuxiliaryStatement::Recursive)
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
@@ -10,22 +10,14 @@ module Torque
|
|
10
10
|
# :nodoc:
|
11
11
|
def auxiliary_statements_values=(value); set_value(:auxiliary_statements, value); end
|
12
12
|
|
13
|
-
# Set use of an auxiliary statement
|
14
|
-
def with(*args)
|
15
|
-
spawn.with!(*args)
|
13
|
+
# Set use of an auxiliary statement
|
14
|
+
def with(*args, **settings)
|
15
|
+
spawn.with!(*args, **settings)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Like #with, but modifies relation in place.
|
19
|
-
def with!(*args)
|
20
|
-
|
21
|
-
args.each do |table|
|
22
|
-
instance = table.is_a?(Class) && table < PostgreSQL::AuxiliaryStatement \
|
23
|
-
? table.new(options) \
|
24
|
-
: PostgreSQL::AuxiliaryStatement.instantiate(table, self, options)
|
25
|
-
|
26
|
-
self.auxiliary_statements_values += [instance]
|
27
|
-
end
|
28
|
-
|
19
|
+
def with!(*args, **settings)
|
20
|
+
instantiate_auxiliary_statements(*args, **settings)
|
29
21
|
self
|
30
22
|
end
|
31
23
|
|
@@ -47,8 +39,23 @@ module Torque
|
|
47
39
|
# Hook arel build to add the distinct on clause
|
48
40
|
def build_arel(*)
|
49
41
|
arel = super
|
50
|
-
|
51
|
-
|
42
|
+
type = auxiliary_statement_type
|
43
|
+
sub_queries = build_auxiliary_statements(arel)
|
44
|
+
sub_queries.nil? ? arel : arel.with(*type, *sub_queries)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Instantiate one or more auxiliary statements for the given +klass+
|
48
|
+
def instantiate_auxiliary_statements(*args, **options)
|
49
|
+
klass = PostgreSQL::AuxiliaryStatement
|
50
|
+
klass = klass::Recursive if options.delete(:recursive).present?
|
51
|
+
|
52
|
+
self.auxiliary_statements_values += args.map do |table|
|
53
|
+
if table.is_a?(Class) && table < klass
|
54
|
+
table.new(**options)
|
55
|
+
else
|
56
|
+
klass.instantiate(table, self, **options)
|
57
|
+
end
|
58
|
+
end
|
52
59
|
end
|
53
60
|
|
54
61
|
# Build all necessary data for auxiliary statements
|
@@ -59,6 +66,12 @@ module Torque
|
|
59
66
|
end
|
60
67
|
end
|
61
68
|
|
69
|
+
# Return recursive if any auxiliary statement is recursive
|
70
|
+
def auxiliary_statement_type
|
71
|
+
klass = PostgreSQL::AuxiliaryStatement::Recursive
|
72
|
+
:recursive if auxiliary_statements_values.any?(klass)
|
73
|
+
end
|
74
|
+
|
62
75
|
# Throw an error showing that an auxiliary statement of the given
|
63
76
|
# table name isn't defined
|
64
77
|
def auxiliary_statement_error(name)
|
@@ -109,7 +109,6 @@ module Torque
|
|
109
109
|
|
110
110
|
# Try to find a model based on a given table
|
111
111
|
def lookup_model(table_name, scoped_class = '')
|
112
|
-
# byebug if table_name == 'activities'
|
113
112
|
scoped_class = scoped_class.name if scoped_class.is_a?(Class)
|
114
113
|
return @data_sources_model_names[table_name] \
|
115
114
|
if @data_sources_model_names.key?(table_name)
|
@@ -118,6 +117,12 @@ module Torque
|
|
118
117
|
scopes = scoped_class.scan(/(?:::)?[A-Z][a-z]+/)
|
119
118
|
scopes.unshift('Object::')
|
120
119
|
|
120
|
+
# Check if the table name comes with a schema
|
121
|
+
if table_name.include?('.')
|
122
|
+
schema, table_name = table_name.split('.')
|
123
|
+
scopes.insert(1, schema.camelize) if schema != 'public'
|
124
|
+
end
|
125
|
+
|
121
126
|
# Consider the maximum namespaced possible model name
|
122
127
|
max_name = table_name.tr('_', '/').camelize.split(/(::)/)
|
123
128
|
max_name[-1] = max_name[-1].singularize
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
class TableName < Delegator
|
6
|
+
def initialize(klass, table_name)
|
7
|
+
@klass = klass
|
8
|
+
@table_name = table_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def schema
|
12
|
+
return @schema if defined?(@schema)
|
13
|
+
|
14
|
+
@schema = ([@klass] + @klass.module_parents[0..-2]).find do |klass|
|
15
|
+
next unless klass.respond_to?(:schema)
|
16
|
+
break klass.schema
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
schema.nil? ? @table_name : "#{schema}.#{@table_name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
alias __getobj__ to_s
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
other.to_s =~ /("?#{schema | search_path_schemes.join('|')}"?\.)?"?#{@table_name}"?/
|
28
|
+
end
|
29
|
+
|
30
|
+
def __setobj__(value)
|
31
|
+
@table_name = value
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def search_path_schemes
|
37
|
+
klass.connection.schemas_search_path_sanitized
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/torque/postgresql.rb
CHANGED
@@ -20,12 +20,13 @@ require 'torque/postgresql/associations'
|
|
20
20
|
require 'torque/postgresql/attributes'
|
21
21
|
require 'torque/postgresql/autosave_association'
|
22
22
|
require 'torque/postgresql/auxiliary_statement'
|
23
|
-
require 'torque/postgresql/base'
|
24
23
|
require 'torque/postgresql/inheritance'
|
24
|
+
require 'torque/postgresql/base' # Needs to be after inheritance
|
25
25
|
require 'torque/postgresql/insert_all'
|
26
26
|
require 'torque/postgresql/migration'
|
27
27
|
require 'torque/postgresql/relation'
|
28
28
|
require 'torque/postgresql/reflection'
|
29
29
|
require 'torque/postgresql/schema_cache'
|
30
|
+
require 'torque/postgresql/table_name'
|
30
31
|
|
31
32
|
require 'torque/postgresql/railtie' if defined?(Rails)
|
data/spec/schema.rb
CHANGED
@@ -20,6 +20,9 @@ ActiveRecord::Schema.define(version: version) do
|
|
20
20
|
enable_extension "pgcrypto"
|
21
21
|
enable_extension "plpgsql"
|
22
22
|
|
23
|
+
# Custom schemas used in this database.
|
24
|
+
create_schema "internal", force: :cascade
|
25
|
+
|
23
26
|
# Custom types defined in this database.
|
24
27
|
# Note that some types may not work with other database engines. Be careful if changing database.
|
25
28
|
create_enum "content_status", ["created", "draft", "published", "archived"]
|
@@ -66,6 +69,11 @@ ActiveRecord::Schema.define(version: version) do
|
|
66
69
|
t.enum "specialty", enum_type: :specialties
|
67
70
|
end
|
68
71
|
|
72
|
+
create_table "categories", force: :cascade do |t|
|
73
|
+
t.integer "parent_id"
|
74
|
+
t.string "title"
|
75
|
+
end
|
76
|
+
|
69
77
|
create_table "texts", force: :cascade do |t|
|
70
78
|
t.integer "user_id"
|
71
79
|
t.string "content"
|
@@ -83,6 +91,7 @@ ActiveRecord::Schema.define(version: version) do
|
|
83
91
|
end
|
84
92
|
|
85
93
|
create_table "courses", force: :cascade do |t|
|
94
|
+
t.integer "category_id"
|
86
95
|
t.string "title", null: false
|
87
96
|
t.interval "duration"
|
88
97
|
t.enum "types", enum_type: :types, array: true
|
@@ -117,6 +126,13 @@ ActiveRecord::Schema.define(version: version) do
|
|
117
126
|
t.datetime "updated_at", null: false
|
118
127
|
end
|
119
128
|
|
129
|
+
create_table "users", schema: "internal", force: :cascade do |t|
|
130
|
+
t.string "email"
|
131
|
+
t.datetime "created_at", null: false
|
132
|
+
t.datetime "updated_at", null: false
|
133
|
+
t.index ["email"], name: "index_internal_users_on_email", unique: true
|
134
|
+
end
|
135
|
+
|
120
136
|
create_table "activities", force: :cascade do |t|
|
121
137
|
t.integer "author_id"
|
122
138
|
t.string "title"
|
data/spec/spec_helper.rb
CHANGED
@@ -22,7 +22,7 @@ cleaner = ->() do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
load File.join('schema.rb')
|
25
|
-
Dir.glob(File.join('spec', '{models,factories,mocks}', '*.rb')) do |file|
|
25
|
+
Dir.glob(File.join('spec', '{models,factories,mocks}', '**', '*.rb')) do |file|
|
26
26
|
require file[5..-4]
|
27
27
|
end
|
28
28
|
|
@@ -39,6 +39,7 @@ RSpec.configure do |config|
|
|
39
39
|
|
40
40
|
# Handles acton before rspec initialize
|
41
41
|
config.before(:suite) do
|
42
|
+
Torque::PostgreSQL.config.schemas.whitelist << 'internal'
|
42
43
|
ActiveSupport::Deprecation.silenced = true
|
43
44
|
DatabaseCleaner.clean_with(:truncation)
|
44
45
|
end
|