torque-postgresql 3.0.1 → 3.2.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.
- 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
|