Class: ClosureTree::Support

Inherits:
Object
  • Object
show all
Includes:
ActiveRecordSupport, HashTreeSupport, SupportAttributes, SupportFlags
Defined in:
lib/closure_tree/support.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from HashTreeSupport

#build_hash_tree, #default_tree_scope, #hash_tree

Methods included from ActiveRecordSupport

#ensure_fixed_table_name, #quote, #remove_prefix_and_suffix

Methods included from SupportAttributes

#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

Methods included from SupportFlags

#has_inheritance_column?, #has_name?, #include_forbidden_attributes_protection?, #order_is_numeric?, #order_option?, #subclass?, #use_attr_accessible?

Constructor Details

#initialize(model_class, options) ⇒ Support

Returns a new instance of Support.

Raises:

  • (ArgumentError)


19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/closure_tree/support.rb', line 19

def initialize(model_class, options)
  @model_class = model_class
  @options = {
    :parent_column_name => 'parent_id',
    :dependent => :nullify, # or :destroy or :delete_all -- see the README
    :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 order_is_numeric?
    extend NumericOrderSupport.adapter_for_connection(connection)
  end
end

Instance Attribute Details

#model_classObject (readonly)

Returns the value of attribute model_class.



16
17
18
# File 'lib/closure_tree/support.rb', line 16

def model_class
  @model_class
end

#optionsObject (readonly)

Returns the value of attribute options.



17
18
19
# File 'lib/closure_tree/support.rb', line 17

def options
  @options
end

Instance Method Details

#belongs_to_with_optional_option(opts) ⇒ Object



82
83
84
# File 'lib/closure_tree/support.rb', line 82

def belongs_to_with_optional_option(opts)
  [ActiveRecord::VERSION::MAJOR < 5 ? opts.except(:optional) : opts]
end

#build_ancestry_attr_path(path, attributes) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
# File 'lib/closure_tree/support.rb', line 121

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] ||= self.sti_name
    end
    path = path.map { |ea| attributes.merge(name_column => ea) }
  end
  path
end

#create(model_class, attributes) ⇒ Object



166
167
168
# File 'lib/closure_tree/support.rb', line 166

def create(model_class, attributes)
  creator_class(model_class, attributes.with_indifferent_access[inheritance_column]).new(attributes)
end

#create!(model_class, attributes) ⇒ Object



170
171
172
# File 'lib/closure_tree/support.rb', line 170

def create!(model_class, attributes)
  create(model_class, attributes).tap { |ea| ea.save! }
end

#creator_class(model_class, sti_class) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/closure_tree/support.rb', line 158

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



147
148
149
150
151
152
153
154
155
156
# File 'lib/closure_tree/support.rb', line 147

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_with_order_option(opts) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/closure_tree/support.rb', line 91

def has_many_with_order_option(opts)
  order_options = [opts[:order], order_by].compact
  [lambda {
    order_options = order_options.map { |o| o.is_a?(Proc) ? o.call : o }
    order(order_options)
  }, opts.except(:order)]
end

#has_many_without_order_option(opts) ⇒ Object

lambda-ize the order, but don’t apply the default order_option



87
88
89
# File 'lib/closure_tree/support.rb', line 87

def has_many_without_order_option(opts)
  [lambda { order(opts[:order].call) }, opts.except(:order)]
end

#hierarchy_class_for_modelObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/closure_tree/support.rb', line 34

def hierarchy_class_for_model
  parent_class = ActiveSupport::VERSION::MAJOR >= 6 ? model_class.module_parent : model_class.parent
  hierarchy_class = parent_class.const_set(short_hierarchy_class_name, Class.new(ActiveRecord::Base))
  use_attr_accessible = use_attr_accessible?
  include_forbidden_attributes_protection = include_forbidden_attributes_protection?
  model_class_name = model_class.to_s
  hierarchy_class.class_eval do
    include ActiveModel::ForbiddenAttributesProtection if include_forbidden_attributes_protection
    belongs_to :ancestor, class_name: model_class_name
    belongs_to :descendant, class_name: model_class_name
    attr_accessible :ancestor, :descendant, :generations if use_attr_accessible
    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_nameObject



57
58
59
60
61
62
63
64
65
# File 'lib/closure_tree/support.rb', line 57

def hierarchy_table_name
  # We need to use the table_name, not something like ct_class.to_s.demodulize + "_hierarchies",
  # because they may have overridden the table name, which is what we want to be consistent with
  # in order for the schema to make sense.
  tablename = options[:hierarchy_table_name] ||
    remove_prefix_and_suffix(table_name).singularize + "_hierarchies"

  ActiveRecord::Base.table_name_prefix + tablename + ActiveRecord::Base.table_name_suffix
end

#ids_from(scope) ⇒ Object



99
100
101
# File 'lib/closure_tree/support.rb', line 99

def ids_from(scope)
  scope.pluck(model_class.primary_key)
end

#max_join_tablesObject



142
143
144
145
# File 'lib/closure_tree/support.rb', line 142

def max_join_tables
  # MySQL doesn't support more than 61 joined tables (!!):
  50
end

#scope_with_order(scope, additional_order_by = nil) ⇒ Object



74
75
76
77
78
79
80
# File 'lib/closure_tree/support.rb', line 74

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



133
134
135
136
137
138
139
140
# File 'lib/closure_tree/support.rb', line 133

def scoped_attributes(scope, attributes, target_table = model_class.table_name)
  table_prefixed_attributes = Hash[
    attributes.map do |column_name, column_value|
      ["#{target_table}.#{column_name}", column_value]
    end
  ]
  scope.where(table_prefixed_attributes)
end

#where_eq(column_name, value) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/closure_tree/support.rb', line 103

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



111
112
113
114
115
116
117
118
119
# File 'lib/closure_tree/support.rb', line 111

def with_advisory_lock(&block)
  if options[:with_advisory_lock]
    model_class.with_advisory_lock(advisory_lock_name) do
      transaction { yield }
    end
  else
    yield
  end
end

#with_order_option(opts) ⇒ Object



67
68
69
70
71
72
# File 'lib/closure_tree/support.rb', line 67

def with_order_option(opts)
  if order_option?
    opts[:order] = [opts[:order], order_by].compact.join(",")
  end
  opts
end