Class: Tag

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
lib/taxonomy/tag.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#skip_before_destroyObject

Returns the value of attribute skip_before_destroy.



4
5
6
# File 'lib/taxonomy/tag.rb', line 4

def skip_before_destroy
  @skip_before_destroy
end

Class Method Details

.all_roots_valid?Boolean

Returns:

  • (Boolean)


115
116
117
118
119
120
121
122
123
# File 'lib/taxonomy/tag.rb', line 115

def self.all_roots_valid?
  left = right = 0
  roots.all? do |root|
    g_returning(root.left > left && root.right > right) do
      left = root.left
      right = root.right
    end
  end
end

.find_context_with_slug!(context, slug) ⇒ Object

Raises:

  • (ActiveRecord::RecordNotFound)


65
66
67
68
69
# File 'lib/taxonomy/tag.rb', line 65

def self.find_context_with_slug!(context, slug)
  ret = self.where(:context => context, :slug => slug).first
  raise ActiveRecord::RecordNotFound if ret.nil?
  ret
end

.find_or_create_all_with_like_by_name(context, *list) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/taxonomy/tag.rb', line 75

def self.find_or_create_all_with_like_by_name(context, *list)
  list = [list].flatten
  
  return [] if list.empty?
  
  existing_tags = Tag.named_any(context, list).all
  new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.downcase == name.downcase } }
  created_tags  = new_tag_names.map { |name| Tag.create(:context => "#{context.singularize.to_s}", :name => name) }

  existing_tags + created_tags    
end

.find_or_create_with_like_by_name(context, name) ⇒ Object



71
72
73
# File 'lib/taxonomy/tag.rb', line 71

def self.find_or_create_with_like_by_name(context, name)
  named_like(context, name).first || create(:context => "#{context.singularize.to_s}", :name => name)
end

.left_and_rights_valid?Boolean

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/taxonomy/tag.rb', line 95

def self.left_and_rights_valid?
  self.base_class.joins("LEFT OUTER JOIN #{quoted_table_name} AS parent ON " +
      "#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:parent_column])} = parent.#{primary_key}").where(
      "#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} IS NULL OR " +
      "#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])} IS NULL OR " +
      "#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} >= " +
        "#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])} OR " +
      "(#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:parent_column])} IS NOT NULL AND " +
        "(#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} <= parent.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} OR " +
        "#{quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])} >= parent.#{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])}))"
  ).count == 0
end

.no_duplicates_for_columns?Boolean

Returns:

  • (Boolean)


108
109
110
111
112
113
# File 'lib/taxonomy/tag.rb', line 108

def self.no_duplicates_for_columns?
  [connection.quote_column_name(Taxonomy.nested_set_options[:left_column]), connection.quote_column_name(Taxonomy.nested_set_options[:right_column])].all? do |column|
    # No duplicates
    self.base_class.select("#{column}, COUNT(#{column})").group("#{column} HAVING COUNT(#{column}) > 1").first.nil?
  end
end

.rebuild!Object

Rebuilds the left & rights if unset or invalid. Also very useful for converting from acts_as_tree.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/taxonomy/tag.rb', line 126

def self.rebuild!
  # Don't rebuild a valid tree.
  return true if valid?
  
  scope = lambda{|node|}
  if Taxonomy.nested_set_options[:scope]
    scope = lambda{|node| 
      scope_column_names.inject(""){|str, column_name|
        str << "AND #{connection.quote_column_name(column_name)} = #{connection.quote(node.send(column_name.to_sym))} "
      }
    }
  end
  indices = {}
  
  set_left_and_rights = lambda do |node|
    # set left
    node[Taxonomy.nested_set_options[:left_column]] = indices[scope.call(node)] += 1
    # find
    find(:all, :conditions => ["#{connection.quote_column_name(Taxonomy.nested_set_options[:parent_column])} = ? #{scope.call(node)}", node], :order => "#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])}, #{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])}, id").each{|n| set_left_and_rights.call(n) }
    # set right
    node[Taxonomy.nested_set_options[:right_column]] = indices[scope.call(node)] += 1    
    node.save!    
  end
                      
  # Find root node(s)
  root_nodes = find(:all, :conditions => "#{connection.quote_column_name(Taxonomy.nested_set_options[:parent_column])} IS NULL", :order => "#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])}, #{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])}, id").each do |root_node|
    # setup index for this scope
    indices[scope.call(root_node)] ||= 0
    set_left_and_rights.call(root_node)
  end
end

.rootObject

CLASS METHODS:



61
62
63
# File 'lib/taxonomy/tag.rb', line 61

def self.root
  roots.first
end

.treed_taggings_for(context, options = {}) ⇒ Object



91
92
93
# File 'lib/taxonomy/tag.rb', line 91

def self.treed_taggings_for(context, options = {})
  self.where("tags.context" => context.to_s.singularize)
end

.valid?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/taxonomy/tag.rb', line 87

def self.valid?
  left_and_rights_valid? && no_duplicates_for_columns? && all_roots_valid?
end

Instance Method Details

#==(object) ⇒ Object

INSTANCE METHODS:



160
161
162
# File 'lib/taxonomy/tag.rb', line 160

def ==(object)
  super || (object.is_a?(Tag) && name == object.name)
end

#ancestorsObject

Returns an array of all parents



244
245
246
# File 'lib/taxonomy/tag.rb', line 244

def ancestors
  without_self(self_and_ancestors) 
end

#child?Boolean

Returns true is this is a child node

Returns:

  • (Boolean)


204
205
206
# File 'lib/taxonomy/tag.rb', line 204

def child?
  !parent_id.nil?
end

#countObject



168
169
170
# File 'lib/taxonomy/tag.rb', line 168

def count
  read_attribute(:count).to_i
end

#descendantsObject

Returns a set of all of its children and nested children



239
240
241
# File 'lib/taxonomy/tag.rb', line 239

def descendants
  without_self(self_and_descendants) 
end

#is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


301
302
303
304
# File 'lib/taxonomy/tag.rb', line 301

def is_ancestor_of?(other)
  self.reload
  self.left < other.left && other.left < self.right && same_scope?(other)
end

#is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


291
292
293
294
# File 'lib/taxonomy/tag.rb', line 291

def is_descendant_of?(other)
  other.reload
  other.left < self.left && self.left < other.right && same_scope?(other)
end

#is_or_is_ancestor_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


306
307
308
309
# File 'lib/taxonomy/tag.rb', line 306

def is_or_is_ancestor_of?(other)
  self.reload
  self.left <= other.left && other.left < self.right && same_scope?(other)
end

#is_or_is_descendant_of?(other) ⇒ Boolean

Returns:

  • (Boolean)


296
297
298
299
# File 'lib/taxonomy/tag.rb', line 296

def is_or_is_descendant_of?(other)
  other.reload
  other.left <= self.left && self.left < other.right && same_scope?(other)
end

#leaf?Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/taxonomy/tag.rb', line 199

def leaf?
  !new_record? && right - left == 1
end

#leavesObject

Returns a set of all of its nested children which do not have children



234
235
236
# File 'lib/taxonomy/tag.rb', line 234

def leaves
  descendants.where "#{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])} - #{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} = 1"
end

#leftObject

Value of the left column



184
185
186
# File 'lib/taxonomy/tag.rb', line 184

def left
  self[Taxonomy.nested_set_options[:left_column]]
end

#left_siblingObject

Find the first sibling to the left



259
260
261
262
# File 'lib/taxonomy/tag.rb', line 259

def left_sibling
  siblings.where("#{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} < ?", left).order(
    "#{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} DESC").first
end

#levelObject

Returns the level of this object in the tree root level is 0



195
196
197
# File 'lib/taxonomy/tag.rb', line 195

def level
  parent_id.nil? ? 0 : ancestors.count
end

#move_leftObject

Shorthand method for finding the left sibling and moving to the left of it.



278
279
280
# File 'lib/taxonomy/tag.rb', line 278

def move_left
  move_to_left_of left_sibling
end

#move_possible?(target) ⇒ Boolean

Returns:

  • (Boolean)


312
313
314
315
316
317
318
# File 'lib/taxonomy/tag.rb', line 312

def move_possible?(target)
  self != target && # Can't target self
  same_scope?(target) && # can't be in different scopes
  # !(left..right).include?(target.left..target.right) # this needs tested more
  # detect impossible move
  !((left <= target.left && right >= target.left) or (left <= target.right && right >= target.right))
end

#move_rightObject

Shorthand method for finding the right sibling and moving to the right of it.



282
283
284
# File 'lib/taxonomy/tag.rb', line 282

def move_right
  move_to_right_of right_sibling
end

#move_to_child_of(node) ⇒ Object

Move the node to the child of another node (you can pass id only)



254
255
256
# File 'lib/taxonomy/tag.rb', line 254

def move_to_child_of(node)
  move_to node, :child
end

#move_to_left_of(node) ⇒ Object

Move the node to the left of another node (you can pass id only)



270
271
272
# File 'lib/taxonomy/tag.rb', line 270

def move_to_left_of(node)
  move_to node, :left
end

#move_to_right_of(node) ⇒ Object

Move the node to the left of another node (you can pass id only)



274
275
276
# File 'lib/taxonomy/tag.rb', line 274

def move_to_right_of(node)
  move_to node, :right
end

#move_to_rootObject

Move the node to root nodes



287
288
289
# File 'lib/taxonomy/tag.rb', line 287

def move_to_root
  move_to nil, :root
end

#parent_idObject

Value of the parent column



179
180
181
# File 'lib/taxonomy/tag.rb', line 179

def parent_id
  self[Taxonomy.nested_set_options[:parent_column]]
end

#rightObject

Value of the right column



189
190
191
# File 'lib/taxonomy/tag.rb', line 189

def right
  self[Taxonomy.nested_set_options[:right_column]]
end

#right_siblingObject

Find the first sibling to the right



265
266
267
# File 'lib/taxonomy/tag.rb', line 265

def right_sibling
  siblings.where("#{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} > ?", left).first
end

#root?Boolean

Returns true if this is a root node

Returns:

  • (Boolean)


175
176
177
# File 'lib/taxonomy/tag.rb', line 175

def root?
  parent_id.nil?
end

#same_scope?(other) ⇒ Boolean

Check if other model is in the same scope

Returns:

  • (Boolean)


227
228
229
230
231
# File 'lib/taxonomy/tag.rb', line 227

def same_scope?(other)
  Array(Taxonomy.nested_set_options[:scope]).all? do |attr|
    self.send(attr) == other.send(attr)
  end
end

#self_and_ancestorsObject

Returns the array of all parents and self



209
210
211
212
# File 'lib/taxonomy/tag.rb', line 209

def self_and_ancestors
  self.reload
  nested_set_scope.where("#{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} <= ? AND #{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])} >= ?", left, right)
end

#self_and_descendantsObject

Returns a set of itself and all of its nested children



215
216
217
218
# File 'lib/taxonomy/tag.rb', line 215

def self_and_descendants
  self.reload
  nested_set_scope.where("#{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:left_column])} >= ? AND #{self.class.quoted_table_name}.#{connection.quote_column_name(Taxonomy.nested_set_options[:right_column])} <= ?", left, right)
end

#self_and_siblingsObject

Returns the scope of all children of the parent, including self



221
222
223
224
# File 'lib/taxonomy/tag.rb', line 221

def self_and_siblings
  # Rails 3, but not really. scoped.where(Taxonomy.nested_set_options[:parent_column] => parent_id)
  nested_set_scope.where(["#{Taxonomy.nested_set_options[:parent_column]} == #{parent_id}"])
end

#siblingsObject

Returns the array of all children of the parent, except self



249
250
251
# File 'lib/taxonomy/tag.rb', line 249

def siblings
  without_self(self_and_siblings)
end

#to_sObject



164
165
166
# File 'lib/taxonomy/tag.rb', line 164

def to_s
  name
end