Module: Glue::Taggable

Includes:
Og::EntityMixin
Defined in:
lib/glue/taggable.rb,
lib/glue/taggable_old.rb

Overview

Add tagging methods to the target class. For more information on the algorithms used surf: www.pui.ch/phred/archives/2005/04/tags-database-schemas.html

Example

class Article

  include Taggable
  ..
end

article.tag(‘navel’, ‘gmosx’, ‘nitro’) article.tags article.tag_names Article.find_with_tags(‘navel’, ‘gmosx’) Article.find_with_any_tag(‘name’, ‘gmosx’)

Article::Tag.find_by_name(‘ruby’).articles

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Og::EntityMixin

#assign_properties, #delete, #insert, #og_quote, #reload, #save, #saved?, #transaction, #update, #update_by_sql, #update_properties

Class Method Details

.append_dynamic_features(base, options = nil) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/glue/taggable_old.rb', line 65

def self.append_dynamic_features(base, options = nil)
  o = { 
    :base_tag_class => Og::Tag,
    :separator => ' '
  }
  o.update(options) if options

  base.send :include, Og::RelationDSL

  code = ''
 
  unless o[:tag_class]
    o[:tag_class] = 'Tag'
    code << %{
      class #{o[:tag_class]} < #{o[:base_tag_class]}
        many_to_many #{base}, :foreign_name => :tags
      end
    }
  end
  
  tag_class = o[:tag_class]
  separator = o[:separator].inspect
  
  code << %{
    many_to_many :tags, #{o[:tag_class]}
   }

  # Instance Methods:
   
  # Apply a collection of tags to the object.
  # +tags+ can be either an array or a String.
  #
  # === Options
  #
  # +clear+: clear the tags collection before adding the
  #           new tags.

  code << %{      
    def tag(the_tags, options = {})
      return unless the_tags
      
      options = {
        :clear => true
      }.merge(options)
      
      names = Taggable.tags_to_names(the_tags, #{separator})
      
      tags.load_members
      
      # clear the collection if needed.    
      
      self.tags.clear if options[:clear]

      # append the tag names to the collection    
      
      names.each do |name| 
        name = name.strip
        unless tagged_with?(name)
          unless tag_obj = #{tag_class}.find_by_name(name) 
            tag_obj = #{tag_class}.create(name)
          end
           tags << tag_obj
         end
      end
      
      self.save
    end
  }

  # Clears all tags.
  
  def delete_tags
  end
  alias_method :clear_tags, :delete_tags
  
  # Returns an array of strings containing the tags applied to 
  # this object.
  
  code << %{  
    def tag_names
      tags.map { |t| t.name }
    end
  }  

  # Checks to see if this object has been tagged 
  # with +tag_name+.

  code << %{      
    def tagged_with?(tag_name)
      tag_names.include?(tag_name)
    end
    alias_method :tagged_by?, :tagged_with?      
  }

  # Class Methods:
  
  code << %{
    class << self
  }
  
  # Find objects with all of the provided tags.
  # INTERSECTION (AND)
  #--
  # TODO: move info out of compiled code.
  #++

  code << %{
    def find_with_tags(*names)
      info = ogmanager.store.join_table_info(#{base}, #{tag_class})
      count = names.size
      names = names.map { |n| "'\#{n}'" }.join(',')
      sql = %{        
        SELECT o.* 
        FROM 
          \#{info[:first_table]} AS o, 
          \#{info[:second_table]} as t, 
          \#{info[:table]} as j  
        WHERE o.oid = j.\#{info[:first_key]} 
          AND t.oid = j.\#{info[:second_key]} 
          AND (t.name in (\#{names}))
        GROUP BY o.oid
        HAVING COUNT(o.oid) = \#{count};
      }
      return self.select(sql)
    end
    alias_method :find_with_tag, :find_with_tags
  }

  # Find objects with any of the provided tags.
  # UNION (OR)

  code << %{
    def find_with_any_tag(*names)
      info = ogmanager.store.join_table_info(#{base}, #{tag_class})
      count = names.size
      names = names.map { |n| "'\#{n}'" }.join(',')
      sql = %{        
        SELECT o.* 
        FROM 
          \#{info[:first_table]} AS o, 
          \#{info[:second_table]} as t, 
          \#{info[:table]} as j  
        WHERE 
          o.oid = j.\#{info[:first_key]} 
          AND t.oid = j.\#{info[:second_key]} 
          AND (t.name in (\#{names}))
        GROUP BY o.oid
      }
      return self.select(sql)
    end
  }
      
  code << %{
    end
  }
  
  base.module_eval(code)
end

.included(base) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
# File 'lib/glue/taggable.rb', line 147

def self.included(base)
  Tag.module_eval do
    many_to_many base
  end
  base.extend(ClassMethods)
  #--
  # FIXME: Og should handle this automatically.
  #++
  base.send :include, Aspects
  base.before 'tags.clear', :on => [:og_delete]
end

.tags_to_names(the_tags, separator = ' ') ⇒ Object

Helper.



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/glue/taggable.rb', line 161

def self.tags_to_names(the_tags, separator = ' ')
  if the_tags.is_a? Array
    names = the_tags 
  elsif the_tags.is_a? String
    names = the_tags.split(separator)
  end
  
  names = names.flatten.uniq.compact
  
  return names
end

Instance Method Details

#delete_tagsObject

Clears all tags.



136
137
# File 'lib/glue/taggable_old.rb', line 136

def delete_tags
end

#tag(the_tags, options = {}) ⇒ Object Also known as: tag!

Add a tag for this object.



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/glue/taggable.rb', line 73

def tag(the_tags, options = {})
  options = {
    :clear => true
  }.merge(options)
    
  self.tags.clear if options[:clear]
  
  for name in Taggable.tags_to_names(the_tags)
    Tag.find_or_create_by_name(name).tag(self)
  end    
end

#tag_namesObject

Return the names of the tags.



88
89
90
# File 'lib/glue/taggable.rb', line 88

def tag_names
  tags.collect { |t| t.name }
end

#tagged_with?(tag_name) ⇒ Boolean Also known as: tagged_by?

Checks to see if this object has been tagged with tag_name.

Returns:

  • (Boolean)


95
96
97
# File 'lib/glue/taggable.rb', line 95

def tagged_with?(tag_name)
  tag_names.include?(tag_name)
end