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
-
#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, #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
|
# 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,
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
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)
41
42
43
44
45
46
47
48
49
50
51
52
|
# File 'lib/closure_tree/support.rb', line 41
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
254
255
256
257
258
259
|
# File 'lib/closure_tree/support.rb', line 254
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
165
166
167
168
169
170
171
172
173
174
175
|
# File 'lib/closure_tree/support.rb', line 165
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
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# File 'lib/closure_tree/support.rb', line 141
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
209
210
211
|
# File 'lib/closure_tree/support.rb', line 209
def create(model_class, attributes)
creator_class(model_class, attributes.with_indifferent_access[inheritance_column]).new(attributes)
end
|
#create!(model_class, attributes) ⇒ Object
213
214
215
|
# File 'lib/closure_tree/support.rb', line 213
def create!(model_class, attributes)
create(model_class, attributes).tap(&:save!)
end
|
#creator_class(model_class, sti_class) ⇒ Object
201
202
203
204
205
206
207
|
# File 'lib/closure_tree/support.rb', line 201
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
189
190
191
192
193
194
195
196
197
198
199
|
# File 'lib/closure_tree/support.rb', line 189
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
119
120
121
122
123
124
125
|
# File 'lib/closure_tree/support.rb', line 119
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
115
116
117
|
# File 'lib/closure_tree/support.rb', line 115
def has_many_order_without_option(order_by_opt)
[-> { order(order_by_opt.call) }]
end
|
#hierarchy_class_for_model ⇒ Object
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# File 'lib/closure_tree/support.rb', line 54
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# File 'lib/closure_tree/support.rb', line 80
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
127
128
129
|
# File 'lib/closure_tree/support.rb', line 127
def ids_from(scope)
scope.pluck(model_class.primary_key)
end
|
#max_join_tables ⇒ Object
184
185
186
187
|
# File 'lib/closure_tree/support.rb', line 184
def max_join_tables
50
end
|
#scope_columns ⇒ Object
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
# File 'lib/closure_tree/support.rb', line 217
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
# File 'lib/closure_tree/support.rb', line 232
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
106
107
108
109
110
111
112
|
# File 'lib/closure_tree/support.rb', line 106
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
177
178
179
180
181
182
|
# File 'lib/closure_tree/support.rb', line 177
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
131
132
133
134
135
136
137
|
# File 'lib/closure_tree/support.rb', line 131
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
155
156
157
158
159
160
161
162
163
|
# File 'lib/closure_tree/support.rb', line 155
def with_advisory_lock(&block)
if options[:with_advisory_lock] && connection.supports_advisory_locks? && model_class.respond_to?(:with_advisory_lock)
model_class.with_advisory_lock(advisory_lock_name) do
transaction(&block)
end
else
yield
end
end
|
#with_order_option(opts) ⇒ Object
101
102
103
104
|
# File 'lib/closure_tree/support.rb', line 101
def with_order_option(opts)
opts[:order] = [opts[:order], order_by].compact.join(',') if order_option?
opts
end
|