torque-postgresql 0.2.16 → 1.0.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 +76 -3
- data/lib/torque-postgresql.rb +1 -0
- data/lib/torque/postgresql.rb +6 -0
- data/lib/torque/postgresql/adapter.rb +2 -4
- data/lib/torque/postgresql/adapter/database_statements.rb +23 -9
- data/lib/torque/postgresql/adapter/oid.rb +12 -1
- data/lib/torque/postgresql/adapter/oid/box.rb +28 -0
- data/lib/torque/postgresql/adapter/oid/circle.rb +37 -0
- data/lib/torque/postgresql/adapter/oid/enum.rb +9 -5
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +44 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +59 -0
- data/lib/torque/postgresql/adapter/oid/range.rb +52 -0
- data/lib/torque/postgresql/adapter/oid/segment.rb +73 -0
- data/lib/torque/postgresql/adapter/quoting.rb +21 -0
- data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +10 -1
- data/lib/torque/postgresql/arel.rb +3 -0
- data/lib/torque/postgresql/arel/infix_operation.rb +42 -0
- data/lib/torque/postgresql/arel/nodes.rb +32 -0
- data/lib/torque/postgresql/arel/operations.rb +18 -0
- data/lib/torque/postgresql/arel/visitors.rb +28 -2
- data/lib/torque/postgresql/associations.rb +8 -0
- data/lib/torque/postgresql/associations/association.rb +30 -0
- data/lib/torque/postgresql/associations/association_scope.rb +116 -0
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +117 -0
- data/lib/torque/postgresql/associations/builder.rb +2 -0
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +121 -0
- data/lib/torque/postgresql/associations/builder/has_many.rb +15 -0
- data/lib/torque/postgresql/associations/join_dependency/join_association.rb +15 -0
- data/lib/torque/postgresql/associations/preloader.rb +25 -0
- data/lib/torque/postgresql/associations/preloader/association.rb +64 -0
- data/lib/torque/postgresql/attributes.rb +2 -0
- data/lib/torque/postgresql/attributes/builder.rb +1 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +23 -15
- data/lib/torque/postgresql/attributes/builder/period.rb +452 -0
- data/lib/torque/postgresql/attributes/enum.rb +11 -8
- data/lib/torque/postgresql/attributes/enum_set.rb +256 -0
- data/lib/torque/postgresql/attributes/lazy.rb +1 -1
- data/lib/torque/postgresql/attributes/period.rb +31 -0
- data/lib/torque/postgresql/attributes/type_map.rb +3 -5
- data/lib/torque/postgresql/autosave_association.rb +40 -0
- data/lib/torque/postgresql/auxiliary_statement.rb +201 -198
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +20 -12
- data/lib/torque/postgresql/base.rb +161 -2
- data/lib/torque/postgresql/config.rb +91 -9
- data/lib/torque/postgresql/geometry_builder.rb +92 -0
- data/lib/torque/postgresql/i18n.rb +1 -1
- data/lib/torque/postgresql/railtie.rb +18 -5
- data/lib/torque/postgresql/reflection.rb +21 -0
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +109 -0
- data/lib/torque/postgresql/reflection/association_reflection.rb +30 -0
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +44 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +13 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +12 -0
- data/lib/torque/postgresql/reflection/through_reflection.rb +11 -0
- data/lib/torque/postgresql/relation.rb +11 -10
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +11 -18
- data/lib/torque/postgresql/relation/inheritance.rb +2 -2
- data/lib/torque/postgresql/relation/merger.rb +11 -7
- data/lib/torque/postgresql/schema_cache.rb +1 -1
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/range.rb +40 -0
- metadata +41 -9
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'active_record/associations/collection_association'
|
2
|
+
# FIXME: build, create
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
module Associations
|
6
|
+
class BelongsToManyAssociation < ::ActiveRecord::Associations::CollectionAssociation
|
7
|
+
include ::ActiveRecord::Associations::ForeignAssociation
|
8
|
+
|
9
|
+
def handle_dependency
|
10
|
+
case options[:dependent]
|
11
|
+
when :restrict_with_exception
|
12
|
+
raise ::ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
|
13
|
+
|
14
|
+
when :restrict_with_error
|
15
|
+
unless empty?
|
16
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
17
|
+
owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
|
18
|
+
throw(:abort)
|
19
|
+
end
|
20
|
+
|
21
|
+
when :destroy
|
22
|
+
# No point in executing the counter update since we're going to destroy the parent anyway
|
23
|
+
load_target.each { |t| t.destroyed_by_association = reflection }
|
24
|
+
destroy_all
|
25
|
+
else
|
26
|
+
delete_all
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def ids_reader
|
31
|
+
owner[reflection.active_record_primary_key]
|
32
|
+
end
|
33
|
+
|
34
|
+
def ids_writer(new_ids)
|
35
|
+
column = reflection.active_record_primary_key
|
36
|
+
owner.update_column(column, owner[column] = new_ids.presence)
|
37
|
+
@association_scope = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def insert_record(record, *)
|
41
|
+
super
|
42
|
+
|
43
|
+
attribute = (ids_reader || owner[reflection.active_record_primary_key] = [])
|
44
|
+
attribute.push(record[klass_fk])
|
45
|
+
record
|
46
|
+
end
|
47
|
+
|
48
|
+
def empty?
|
49
|
+
size.zero?
|
50
|
+
end
|
51
|
+
|
52
|
+
def include?(record)
|
53
|
+
list = owner[reflection.active_record_primary_key]
|
54
|
+
ids_reader && ids_reader.include?(record[klass_fk])
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Returns the number of records in this collection, which basically
|
60
|
+
# means count the number of entries in the +primary_key+
|
61
|
+
def count_records
|
62
|
+
ids_reader&.size || (@target ||= []).size
|
63
|
+
end
|
64
|
+
|
65
|
+
# When the idea is to nulligy the association, then just set the owner
|
66
|
+
# +primary_key+ as empty
|
67
|
+
def delete_count(method, scope, ids = nil)
|
68
|
+
ids ||= scope.pluck(klass_fk)
|
69
|
+
scope.delete_all if method == :delete_all
|
70
|
+
remove_stash_records(ids)
|
71
|
+
end
|
72
|
+
|
73
|
+
def delete_or_nullify_all_records(method)
|
74
|
+
delete_count(method, scope)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Deletes the records according to the <tt>:dependent</tt> option.
|
78
|
+
def delete_records(records, method)
|
79
|
+
ids = Array.wrap(records).each_with_object(klass_fk).map(&:[])
|
80
|
+
|
81
|
+
if method == :destroy
|
82
|
+
records.each(&:destroy!)
|
83
|
+
remove_stash_records(ids)
|
84
|
+
else
|
85
|
+
scope = self.scope.where(klass_fk => records)
|
86
|
+
delete_count(method, scope, ids)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def concat_records(*)
|
91
|
+
result = super
|
92
|
+
ids_writer(ids_reader)
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
def remove_stash_records(ids)
|
97
|
+
return if ids_reader.nil?
|
98
|
+
ids_writer(ids_reader - Array.wrap(ids))
|
99
|
+
end
|
100
|
+
|
101
|
+
def klass_fk
|
102
|
+
reflection.foreign_key
|
103
|
+
end
|
104
|
+
|
105
|
+
def difference(a, b)
|
106
|
+
a - b
|
107
|
+
end
|
108
|
+
|
109
|
+
def intersection(a, b)
|
110
|
+
a & b
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
::ActiveRecord::Associations.const_set(:BelongsToManyAssociation, BelongsToManyAssociation)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Torque
|
2
|
+
module PostgreSQL
|
3
|
+
module Associations
|
4
|
+
module Builder
|
5
|
+
class BelongsToMany < ::ActiveRecord::Associations::Builder::CollectionAssociation
|
6
|
+
def self.macro
|
7
|
+
:belongs_to_many
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.valid_options(options)
|
11
|
+
super + [:touch, :optional, :default, :dependent, :primary_key, :required]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.valid_dependent_options
|
15
|
+
[:restrict_with_error, :restrict_with_exception]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.define_callbacks(model, reflection)
|
19
|
+
super
|
20
|
+
add_touch_callbacks(model, reflection) if reflection.options[:touch]
|
21
|
+
add_default_callbacks(model, reflection) if reflection.options[:default]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.define_readers(mixin, name)
|
25
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
26
|
+
def #{name}
|
27
|
+
association(:#{name}).reader
|
28
|
+
end
|
29
|
+
CODE
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.define_writers(mixin, name)
|
33
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
34
|
+
def #{name}=(value)
|
35
|
+
association(:#{name}).writer(value)
|
36
|
+
end
|
37
|
+
CODE
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.add_default_callbacks(model, reflection)
|
41
|
+
model.before_validation ->(o) do
|
42
|
+
o.association(reflection.name).default(&reflection.options[:default])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.add_touch_callbacks(model, reflection)
|
47
|
+
foreign_key = reflection.foreign_key
|
48
|
+
n = reflection.name
|
49
|
+
touch = reflection.options[:touch]
|
50
|
+
|
51
|
+
callback = ->(changes_method) do
|
52
|
+
->(record) do
|
53
|
+
BelongsToMany.touch_record(record, record.send(changes_method), foreign_key,
|
54
|
+
n, touch, belongs_to_touch_method)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
unless reflection.counter_cache_column
|
59
|
+
model.after_create callback.call(:saved_changes), if: :saved_changes?
|
60
|
+
model.after_destroy callback.call(:changes_to_save)
|
61
|
+
end
|
62
|
+
|
63
|
+
model.after_update callback.call(:saved_changes), if: :saved_changes?
|
64
|
+
model.after_touch callback.call(:changes_to_save)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
|
68
|
+
old_foreign_ids = changes[foreign_key] && changes[foreign_key].first
|
69
|
+
|
70
|
+
if old_foreign_ids.present?
|
71
|
+
association = o.association(name)
|
72
|
+
reflection = association.reflection
|
73
|
+
klass = association.klass
|
74
|
+
|
75
|
+
primary_key = reflection.association_primary_key(klass)
|
76
|
+
old_records = klass.find_by(primary_key => old_foreign_ids)
|
77
|
+
|
78
|
+
old_records&.map do |old_record|
|
79
|
+
if touch != true
|
80
|
+
old_record.send(touch_method, touch)
|
81
|
+
else
|
82
|
+
old_record.send(touch_method)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
o.send(name)&.map do |record|
|
88
|
+
if record && record.persisted?
|
89
|
+
if touch != true
|
90
|
+
record.send(touch_method, touch)
|
91
|
+
else
|
92
|
+
record.send(touch_method)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.define_validations(model, reflection)
|
99
|
+
if reflection.options.key?(:required)
|
100
|
+
reflection.options[:optional] = !reflection.options.delete(:required)
|
101
|
+
end
|
102
|
+
|
103
|
+
if reflection.options[:optional].nil?
|
104
|
+
required = model.belongs_to_many_required_by_default
|
105
|
+
else
|
106
|
+
required = !reflection.options[:optional]
|
107
|
+
end
|
108
|
+
|
109
|
+
super
|
110
|
+
|
111
|
+
if required
|
112
|
+
model.validates_presence_of reflection.name, message: :required
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
::ActiveRecord::Associations::Builder.const_set(:BelongsToMany, BelongsToMany)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Torque
|
2
|
+
module PostgreSQL
|
3
|
+
module Associations
|
4
|
+
module JoinDependency
|
5
|
+
module JoinAssociation
|
6
|
+
def build_constraint(_, table, _, foreign_table, _)
|
7
|
+
reflection.build_join_constraint(table, foreign_table)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
::ActiveRecord::Associations::JoinDependency::JoinAssociation.prepend(JoinAssociation)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'preloader/association'
|
2
|
+
|
3
|
+
unless Torque::PostgreSQL::AR521
|
4
|
+
module Torque
|
5
|
+
module PostgreSQL
|
6
|
+
module Associations
|
7
|
+
module Preloader
|
8
|
+
BelongsToMany = Class.new(::ActiveRecord::Associations::Preloader::HasMany)
|
9
|
+
|
10
|
+
def preloader_for(reflection, owners, *)
|
11
|
+
return AlreadyLoaded \
|
12
|
+
if owners.first.association(reflection.name).loaded?
|
13
|
+
|
14
|
+
return BelongsToMany \
|
15
|
+
if reflection.macro.eql?(:belongs_to_many)
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
::ActiveRecord::Associations::Preloader.prepend(Preloader)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Torque
|
2
|
+
module PostgreSQL
|
3
|
+
module Associations
|
4
|
+
module Preloader
|
5
|
+
module Association
|
6
|
+
|
7
|
+
delegate :connected_through_array?, to: :@reflection
|
8
|
+
|
9
|
+
# For reflections connected through an array, make sure to properly
|
10
|
+
# decuple the list of ids and set them as associated with the owner
|
11
|
+
def run(preloader)
|
12
|
+
return super unless connected_through_array?
|
13
|
+
send("run_array_for_#{@reflection.macro}")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Specific run for belongs_many association
|
19
|
+
def run_array_for_belongs_to_many
|
20
|
+
# Add reverse to has_many
|
21
|
+
records = load_records
|
22
|
+
owners.each do |owner|
|
23
|
+
items = records.values_at(*Array.wrap(owner[owner_key_name]))
|
24
|
+
associate_records_to_owner(owner, items.flatten)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specific run for has_many association
|
29
|
+
def run_array_for_has_many
|
30
|
+
# Add reverse to belongs_to_many
|
31
|
+
records = Hash.new { |h, k| h[k] = [] }
|
32
|
+
load_records.each do |ids, record|
|
33
|
+
ids.each { |id| records[id].concat(Array.wrap(record)) }
|
34
|
+
end
|
35
|
+
|
36
|
+
owners.each do |owner|
|
37
|
+
associate_records_to_owner(owner, records[owner[owner_key_name]] || [])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Build correctly the constraint condition in order to get the
|
42
|
+
# associated ids
|
43
|
+
def records_for(ids, &block)
|
44
|
+
return super unless connected_through_array?
|
45
|
+
condition = scope.arel_attribute(association_key_name)
|
46
|
+
condition = reflection.build_id_constraint(condition, ids.flatten.uniq)
|
47
|
+
scope.where(condition).load(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
unless Torque::PostgreSQL::AR521
|
51
|
+
def associate_records_to_owner(owner, records)
|
52
|
+
association = owner.association(reflection.name)
|
53
|
+
association.loaded!
|
54
|
+
association.target.concat(records)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
::ActiveRecord::Associations::Preloader::Association.prepend(Association)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -3,11 +3,9 @@ module Torque
|
|
3
3
|
module Attributes
|
4
4
|
module Builder
|
5
5
|
class Enum
|
6
|
-
|
7
6
|
attr_accessor :klass, :attribute, :subtype, :options, :values, :enum_module
|
8
7
|
|
9
|
-
# Start a new builder of methods for
|
10
|
-
# ActiveRecord::Base
|
8
|
+
# Start a new builder of methods for enum values on ActiveRecord::Base
|
11
9
|
def initialize(klass, attribute, subtype, options)
|
12
10
|
@klass = klass
|
13
11
|
@attribute = attribute.to_s
|
@@ -65,7 +63,7 @@ module Torque
|
|
65
63
|
|
66
64
|
return false
|
67
65
|
rescue Interrupt => err
|
68
|
-
raise ArgumentError, <<-MSG.
|
66
|
+
raise ArgumentError, <<-MSG.squish
|
69
67
|
#{subtype.class.name} was not able to generate requested
|
70
68
|
methods because the method #{err} already exists in
|
71
69
|
#{klass.name}.
|
@@ -77,7 +75,7 @@ module Torque
|
|
77
75
|
@enum_module = Module.new
|
78
76
|
|
79
77
|
plural
|
80
|
-
|
78
|
+
stringify
|
81
79
|
all_values
|
82
80
|
|
83
81
|
klass.include enum_module
|
@@ -103,6 +101,8 @@ module Torque
|
|
103
101
|
def plural
|
104
102
|
attr = attribute
|
105
103
|
enum_klass = subtype.klass
|
104
|
+
|
105
|
+
# TODO: Rewrite these as string
|
106
106
|
enum_module.const_set('ClassMethods', Module.new)
|
107
107
|
enum_module::ClassMethods.module_eval do
|
108
108
|
# def self.statuses() statuses end
|
@@ -126,11 +126,13 @@ module Torque
|
|
126
126
|
|
127
127
|
# Create the method that turn the attribute value into text using
|
128
128
|
# the model scope
|
129
|
-
def
|
129
|
+
def stringify
|
130
130
|
attr = attribute
|
131
|
+
|
132
|
+
# TODO: Rewrite these as string
|
131
133
|
enum_module.module_eval do
|
132
134
|
# def status_text() status.text('status', self) end
|
133
|
-
define_method("#{attr}_text") { send(attr)
|
135
|
+
define_method("#{attr}_text") { send(attr)&.text(attr, self) }
|
134
136
|
end
|
135
137
|
end
|
136
138
|
|
@@ -139,27 +141,33 @@ module Torque
|
|
139
141
|
def all_values
|
140
142
|
attr = attribute
|
141
143
|
vals = values_methods
|
144
|
+
|
145
|
+
enum_klass = subtype.klass
|
142
146
|
model_klass = klass
|
147
|
+
|
148
|
+
# TODO: Rewrite these as string
|
143
149
|
enum_module.module_eval do
|
144
150
|
vals.each do |val, list|
|
145
151
|
# scope :disabled, -> { where(status: 'disabled') }
|
146
|
-
model_klass.scope list[0], ->
|
152
|
+
model_klass.scope list[0], -> do
|
153
|
+
where(enum_klass.scope(arel_table[attr], val))
|
154
|
+
end
|
147
155
|
|
148
156
|
# def disabled? status.disabled? end
|
149
157
|
define_method(list[1]) { send(attr).public_send("#{val}?") }
|
150
158
|
|
151
|
-
# def disabled!
|
159
|
+
# def disabled!
|
160
|
+
# changed = send(attr).public_send("#{val}!")
|
161
|
+
# save! if changed && enum_save_on_bang
|
162
|
+
# true
|
152
163
|
define_method(list[2]) do
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
send(attr).public_send("#{val}!")
|
157
|
-
end
|
164
|
+
changed = send(attr).public_send("#{val}!")
|
165
|
+
return save! if changed && enum_save_on_bang
|
166
|
+
true
|
158
167
|
end
|
159
168
|
end
|
160
169
|
end
|
161
170
|
end
|
162
|
-
|
163
171
|
end
|
164
172
|
end
|
165
173
|
end
|