Class: ClosureTree::Support
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#abstract_base_class ⇒ Object
Find the abstract base class for database connection This ensures hierarchy class uses the same database but doesn’t inherit validations/callbacks from STI parent classes (issue #392).
-
#apply_scope_conditions(scope, instance = nil) ⇒ Object
-
#build_ancestry_attr_path(path, attributes) ⇒ Object
-
#build_scope_where_clause(scope_conditions) ⇒ Object
Builds SQL WHERE conditions for scope columns Returns a string that can be appended to a WHERE clause.
-
#create(model_class, attributes) ⇒ Object
-
#create!(model_class, attributes) ⇒ Object
-
#creator_class(model_class, sti_class) ⇒ Object
-
#find_by_large_path(path, attributes = {}, parent_id = nil) ⇒ Object
-
#has_many_order_with_option(order_by_opt = nil) ⇒ Object
-
#has_many_order_without_option(order_by_opt) ⇒ Object
lambda-ize the order, but don’t apply the default order_option.
-
#hierarchy_class_for_model ⇒ Object
-
#hierarchy_table_name ⇒ Object
-
#ids_from(scope) ⇒ Object
-
#initialize(model_class, options) ⇒ Support
constructor
A new instance of Support.
-
#max_join_tables ⇒ Object
-
#previous_scope_values_from_instance(instance) ⇒ Object
-
#scope_changed?(instance) ⇒ Boolean
-
#scope_columns ⇒ Object
-
#scope_values_from_instance(instance) ⇒ Object
-
#scope_with_order(scope, additional_order_by = nil) ⇒ Object
-
#scoped_attributes(scope, attributes, target_table = model_class.table_name) ⇒ Object
-
#where_eq(column_name, value) ⇒ Object
-
#with_advisory_lock(&block) ⇒ Object
-
#with_order_option(opts) ⇒ Object
#aliased_table, #build_hierarchy_delete_query, #build_hierarchy_insert_query, #hierarchy_table, #hierarchy_table_for, #model_table, #to_sql_with_connection
#build_hash_tree, #default_tree_scope, #hash_tree
#remove_prefix_and_suffix
#advisory_lock_name, #advisory_lock_options, #dont_order_roots, #hierarchy_class_name, #name_column, #name_sym, #nulls_last_order_by, #order_by, #order_by_order, #order_column, #order_column_sym, #parent_column_name, #parent_column_sym, #primary_key_column, #primary_key_type, #quoted_hierarchy_table_name, #quoted_id_column_name, #quoted_name_column, #quoted_order_column, #quoted_parent_column_name, #quoted_table_name, #quoted_value, #require_order_column, #short_hierarchy_class_name, #t_alias_keyword
#has_inheritance_column?, #has_name?, #order_is_numeric?, #order_option?, #subclass?
Constructor Details
#initialize(model_class, options) ⇒ Support
Returns a new instance of Support.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# File 'lib/closure_tree/support.rb', line 14
def initialize(model_class, options)
@model_class = model_class
@options = {
parent_column_name: 'parent_id',
dependent: :nullify, name_column: 'name',
with_advisory_lock: true, advisory_lock_timeout_seconds: nil,
numeric_order: false
}.merge(options)
raise ArgumentError, "name_column can't be 'path'" if options[:name_column] == 'path'
if options[:scope]
scope_option = options[:scope]
unless scope_option.is_a?(Symbol) || (scope_option.is_a?(Array) && scope_option.all? { |item| item.is_a?(Symbol) })
raise ArgumentError, "scope option must be a Symbol or an Array of Symbols (e.g., :user_id or [:user_id, :group_id])"
end
end
if !@options[:with_advisory_lock] && @options[:advisory_lock_timeout_seconds].present?
raise ArgumentError, "advisory_lock_timeout_seconds can't be specified when advisory_lock is disabled"
end
return unless order_is_numeric?
extend NumericOrderSupport.adapter_for_connection(connection)
end
|
Instance Attribute Details
#model_class ⇒ Object
Returns the value of attribute model_class.
12
13
14
|
# File 'lib/closure_tree/support.rb', line 12
def model_class
@model_class
end
|
#options ⇒ Object
Returns the value of attribute options.
12
13
14
|
# File 'lib/closure_tree/support.rb', line 12
def options
@options
end
|
Instance Method Details
#abstract_base_class ⇒ Object
Find the abstract base class for database connection This ensures hierarchy class uses the same database but doesn’t inherit validations/callbacks from STI parent classes (issue #392)
46
47
48
49
50
51
52
53
54
55
56
57
|
# File 'lib/closure_tree/support.rb', line 46
def abstract_base_class
klass = model_class
while klass.superclass != ActiveRecord::Base
parent = klass.superclass
return parent if parent.abstract_class?
return parent if parent.connection_specification_name != parent.superclass.connection_specification_name
klass = parent
end
ActiveRecord::Base
end
|
#apply_scope_conditions(scope, instance = nil) ⇒ Object
299
300
301
302
303
304
|
# File 'lib/closure_tree/support.rb', line 299
def apply_scope_conditions(scope, instance = nil)
return scope unless options[:scope] && instance
scope_values = scope_values_from_instance(instance)
scope_values.any? ? scope.where(scope_values) : scope
end
|
#build_ancestry_attr_path(path, attributes) ⇒ Object
171
172
173
174
175
176
177
178
179
180
181
|
# File 'lib/closure_tree/support.rb', line 171
def build_ancestry_attr_path(path, attributes)
path = path.is_a?(Array) ? path.dup : [path]
unless path.first.is_a?(Hash)
if subclass? && has_inheritance_column?
attributes = attributes.with_indifferent_access
attributes[inheritance_column] ||= sti_name
end
path = path.map { |ea| attributes.merge(name_column => ea) }
end
path
end
|
#build_scope_where_clause(scope_conditions) ⇒ Object
Builds SQL WHERE conditions for scope columns Returns a string that can be appended to a WHERE clause
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# File 'lib/closure_tree/support.rb', line 146
def build_scope_where_clause(scope_conditions)
return '' unless scope_conditions.is_a?(Hash) && scope_conditions.any?
conditions = scope_conditions.map do |column, value|
if value.nil?
"#{connection.quote_column_name(column.to_s)} IS NULL"
else
"#{connection.quote_column_name(column.to_s)} = #{quoted_value(value)}"
end
end
" AND #{conditions.join(' AND ')}"
end
|
#create(model_class, attributes) ⇒ Object
215
216
217
|
# File 'lib/closure_tree/support.rb', line 215
def create(model_class, attributes)
creator_class(model_class, attributes.with_indifferent_access[inheritance_column]).new(attributes)
end
|
#create!(model_class, attributes) ⇒ Object
219
220
221
|
# File 'lib/closure_tree/support.rb', line 219
def create!(model_class, attributes)
create(model_class, attributes).tap(&:save!)
end
|
#creator_class(model_class, sti_class) ⇒ Object
207
208
209
210
211
212
213
|
# File 'lib/closure_tree/support.rb', line 207
def creator_class(model_class, sti_class)
if sti_class.present?
base_class.send(:find_sti_class, sti_class)
else
model_class
end
end
|
#find_by_large_path(path, attributes = {}, parent_id = nil) ⇒ Object
195
196
197
198
199
200
201
202
203
204
205
|
# File 'lib/closure_tree/support.rb', line 195
def find_by_large_path(path, attributes = {}, parent_id = nil)
next_parent_id = parent_id
child = nil
path.in_groups(max_join_tables, false).each do |subpath|
child = model_class.find_by_path(subpath, attributes, next_parent_id)
return nil if child.nil?
next_parent_id = child._ct_id
end
child
end
|
#has_many_order_with_option(order_by_opt = nil) ⇒ Object
124
125
126
127
128
129
130
|
# File 'lib/closure_tree/support.rb', line 124
def has_many_order_with_option(order_by_opt = nil)
order_options = [order_by_opt, order_by].compact
[lambda {
order_options = order_options.map { |o| o.is_a?(Proc) ? o.call : o }
order(order_options)
}]
end
|
#has_many_order_without_option(order_by_opt) ⇒ Object
lambda-ize the order, but don’t apply the default order_option
120
121
122
|
# File 'lib/closure_tree/support.rb', line 120
def has_many_order_without_option(order_by_opt)
[-> { order(order_by_opt.call) }]
end
|
#hierarchy_class_for_model ⇒ Object
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
# File 'lib/closure_tree/support.rb', line 59
def hierarchy_class_for_model
parent_class = model_class.module_parent
hierarchy_class = parent_class.const_set(short_hierarchy_class_name, Class.new(abstract_base_class))
model_class_name = model_class.to_s
hierarchy_class.class_eval do
self.implicit_order_column = 'ancestor_id'
self.primary_key = [:ancestor_id, :descendant_id, :generations]
belongs_to :ancestor, class_name: model_class_name
belongs_to :descendant, class_name: model_class_name
def ==(other)
self.class == other.class && ancestor_id == other.ancestor_id && descendant_id == other.descendant_id
end
alias :eql? :==
def hash
(ancestor_id.hash << 31) ^ descendant_id.hash
end
end
hierarchy_class.table_name = hierarchy_table_name
hierarchy_class
end
|
#hierarchy_table_name ⇒ Object
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
# File 'lib/closure_tree/support.rb', line 85
def hierarchy_table_name
if options[:hierarchy_table_name]
tablename = options[:hierarchy_table_name]
else
base_table = remove_prefix_and_suffix(table_name, model_class)
schema, _, table = base_table.rpartition('.')
if schema.present?
tablename = "#{schema}.#{table.singularize}_hierarchies"
else
tablename = "#{table.singularize}_hierarchies"
end
end
[model_class.table_name_prefix, tablename, model_class.table_name_suffix].join
end
|
#ids_from(scope) ⇒ Object
132
133
134
|
# File 'lib/closure_tree/support.rb', line 132
def ids_from(scope)
scope.pluck(model_class.primary_key)
end
|
#max_join_tables ⇒ Object
190
191
192
193
|
# File 'lib/closure_tree/support.rb', line 190
def max_join_tables
50
end
|
#previous_scope_values_from_instance(instance) ⇒ Object
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
|
# File 'lib/closure_tree/support.rb', line 260
def previous_scope_values_from_instance(instance)
return {} unless options[:scope] && instance
scope_option = options[:scope]
scope_hash = {}
case scope_option
when Symbol
value = instance.attribute_before_last_save(scope_option)
scope_hash[scope_option] = value
when Array
scope_option.each do |item|
if item.is_a?(Symbol)
value = instance.attribute_before_last_save(item)
scope_hash[item] = value
end
end
end
scope_hash
end
|
#scope_changed?(instance) ⇒ Boolean
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
|
# File 'lib/closure_tree/support.rb', line 282
def scope_changed?(instance)
return false unless options[:scope] && instance
scope_option = options[:scope]
case scope_option
when Symbol
instance.saved_change_to_attribute?(scope_option)
when Array
scope_option.any? do |item|
item.is_a?(Symbol) && instance.saved_change_to_attribute?(item)
end
else
false
end
end
|
#scope_columns ⇒ Object
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
# File 'lib/closure_tree/support.rb', line 223
def scope_columns
return [] unless options[:scope]
scope_option = options[:scope]
case scope_option
when Symbol
[scope_option]
when Array
scope_option.select { |item| item.is_a?(Symbol) }
else
[]
end
end
|
#scope_values_from_instance(instance) ⇒ Object
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
|
# File 'lib/closure_tree/support.rb', line 238
def scope_values_from_instance(instance)
return {} unless options[:scope] && instance
scope_option = options[:scope]
scope_hash = {}
case scope_option
when Symbol
value = instance.read_attribute(scope_option)
scope_hash[scope_option] = value
when Array
scope_option.each do |item|
if item.is_a?(Symbol)
value = instance.read_attribute(item)
scope_hash[item] = value
end
end
end
scope_hash
end
|
#scope_with_order(scope, additional_order_by = nil) ⇒ Object
111
112
113
114
115
116
117
|
# File 'lib/closure_tree/support.rb', line 111
def scope_with_order(scope, additional_order_by = nil)
if order_option?
scope.order(*[additional_order_by, order_by].compact)
else
additional_order_by ? scope.order(additional_order_by) : scope
end
end
|
#scoped_attributes(scope, attributes, target_table = model_class.table_name) ⇒ Object
183
184
185
186
187
188
|
# File 'lib/closure_tree/support.rb', line 183
def scoped_attributes(scope, attributes, target_table = model_class.table_name)
table_prefixed_attributes = attributes.transform_keys do |column_name|
"#{target_table}.#{column_name}"
end
scope.where(table_prefixed_attributes)
end
|
#where_eq(column_name, value) ⇒ Object
136
137
138
139
140
141
142
|
# File 'lib/closure_tree/support.rb', line 136
def where_eq(column_name, value)
if value.nil?
"#{connection.quote_column_name(column_name)} IS NULL"
else
"#{connection.quote_column_name(column_name)} = #{quoted_value(value)}"
end
end
|
#with_advisory_lock(&block) ⇒ Object
160
161
162
163
164
165
166
167
168
169
|
# File 'lib/closure_tree/support.rb', line 160
def with_advisory_lock(&block)
lock_method = options[:advisory_lock_timeout_seconds].present? ? :with_advisory_lock! : :with_advisory_lock
if options[:with_advisory_lock] && connection.supports_advisory_locks? && model_class.respond_to?(lock_method)
model_class.public_send(lock_method, advisory_lock_name, advisory_lock_options) do
transaction(&block)
end
else
yield
end
end
|
#with_order_option(opts) ⇒ Object
106
107
108
109
|
# File 'lib/closure_tree/support.rb', line 106
def with_order_option(opts)
opts[:order] = [opts[:order], order_by].compact.join(',') if order_option?
opts
end
|