acts_as_organizable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in acts_as_organizable.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "acts_as_organizable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "acts_as_organizable"
7
+ s.version = ActsAsOrganizable::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Mike Hansen"]
10
+ s.email = ["mhansen@pathlightmedia.com"]
11
+ s.summary = %q{Simpler tagging.}
12
+
13
+ s.rubyforge_project = "acts_as_organizable"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_organizable'
@@ -0,0 +1,133 @@
1
+ # To find a person's available tags for the object we go through owned_tags, which pulls uniq items from join table
2
+
3
+ require "acts_as_organizable/version"
4
+ path = File.expand_path(File.dirname(__FILE__))
5
+ $LOAD_PATH << path unless $LOAD_PATH.include?(path)
6
+ require 'acts_as_organizable/tag'
7
+ require 'acts_as_organizable/tagging'
8
+ require 'acts_as_organizable/core_ext/string'
9
+
10
+ module ActsAsOrganizable
11
+ class TagList < Array
12
+ cattr_accessor :delimiter
13
+ @@delimiter = ','
14
+
15
+ def initialize(list)
16
+ list = list.is_a?(Array) ? list : list.split(@@delimiter).collect(&:strip).reject(&:blank?)
17
+ super
18
+ end
19
+
20
+ def to_s
21
+ join(@@delimiter)
22
+ end
23
+ end
24
+
25
+ module ActiveRecordExtension
26
+ def acts_as_organizable(*kinds)
27
+ # Example: acts_as_organizable :tags, :languages
28
+ class_inheritable_accessor :tag_kinds
29
+ self.tag_kinds = kinds.map(&:to_s).map(&:singularize)
30
+ self.tag_kinds << :tag if kinds.empty?
31
+
32
+ include ActsAsOrganizable::TaggableMethods
33
+ end
34
+
35
+ def acts_as_tagger(opts={})
36
+ class_eval do
37
+ has_many :owned_tags, :through => :owned_taggings, :source => :tag, :class_name => "Tag", :uniq => true
38
+ has_many :owned_taggings, opts.merge(:as => :owner, :dependent => :destroy, :include => :tag, :class_name => "Tagging")
39
+ end
40
+ end
41
+ end
42
+
43
+ module TaggableMethods
44
+ def self.included(klass)
45
+ klass.class_eval do
46
+ include ActsAsOrganizable::TaggableMethods::InstanceMethods
47
+
48
+ has_many :taggings, :as => :taggable, :dependent => :destroy
49
+ has_many :tags, :through => :taggings
50
+ before_save :cache_tags
51
+
52
+ tag_kinds.each do |k|
53
+ # Example: language gets passed in and becomes language_list
54
+ define_method("#{k}_list") { get_tag_list(k) }
55
+ # this is expexting language_list = "spanish, italian"
56
+ define_method("#{k}_list=") { |new_list| set_tag_list(k, new_list.clean_up_tags) }
57
+ end
58
+ end
59
+ end
60
+
61
+ module InstanceMethods
62
+ def set_tag_list(kind, list)
63
+ tag_list = TagList.new(list) # ["spanish", "italian"]
64
+ # @language_list = ["spanish", "italian"]
65
+ instance_variable_set(tag_list_name_for_kind(kind), tag_list)
66
+ end
67
+
68
+ def get_tag_list(kind)
69
+ # set instance variable unless it exists
70
+ set_tag_list(kind, tags.of_kind(kind).map(&:name)) if tag_list_instance_variable(kind).nil?
71
+ tag_list_instance_variable(kind)
72
+ end
73
+
74
+ def save_with_tags(tag_owner = nil)
75
+ self.save # save the parent object first
76
+ tag_kinds.each do |tag_kind|
77
+ delete_unused_tags(tag_kind)
78
+ create_taggings(tag_kind, tag_owner)
79
+ end
80
+ end
81
+
82
+ protected
83
+ def tag_list_name_for_kind(kind)
84
+ "@#{kind}_list"
85
+ end
86
+
87
+ def tag_list_instance_variable(kind)
88
+ instance_variable_get(tag_list_name_for_kind(kind))
89
+ end
90
+
91
+ def delete_unused_tags(tag_kind)
92
+ # if the new list does not have the previous tag, kill the old tag associated to the object
93
+ tags.of_kind(tag_kind).each { |t| tags.delete(t) unless get_tag_list(tag_kind).include?(t.name) }
94
+ end
95
+
96
+ def create_taggings(tag_kind, tag_owner)
97
+ # get tags passed in, such as ["russian", "english"] from @language_list
98
+ previous_tags = tags.of_kind(tag_kind).map(&:name)
99
+ get_tag_list(tag_kind).each do |tag_name|
100
+ tag = Tag.find_or_create_with_name_like_and_kind(tag_name, tag_kind) unless previous_tags.include?(tag_name)
101
+ if tag_owner
102
+ # save taggings to tag_owner passed in explicitly
103
+ # NOTE: this includes visitors - they must be passed in explicitly
104
+ taggings.create!(:tag_id => tag.id, :owner => tag_owner)
105
+ # look for user or owner of object to attach to tagging
106
+ elsif user = (self.respond_to?(:owner) && self.try(:owner)) || (self.respond_to?(:user) && self.try(:user))
107
+ taggings.create!(:tag_id => tag.id, :owner => user)
108
+ else
109
+ raise "No user asociated to tagging. Please specify one."
110
+ end
111
+ end
112
+ end
113
+
114
+ def cache_tags
115
+ tag_kinds.each do |tag_kind|
116
+ t = tag_kind.to_s
117
+ if self.class.column_names.include?("cached_#{t}_list")
118
+ list = get_tag_list(tag_kind).join(", ")
119
+ self["cached_#{t}_list"] = list
120
+ else
121
+ logger.info("
122
+ ************************************************************************************************
123
+ You should consider adding cached_#{t}_list to your #{t} for performance benefits!
124
+ ************************************************************************************************
125
+ ")
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ ActiveRecord::Base.send(:extend, ActsAsOrganizable::ActiveRecordExtension)
@@ -0,0 +1,5 @@
1
+ class String
2
+ def clean_up_tags
3
+ gsub(/,/, '').gsub(' ', ', ')
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ class Tag < ActiveRecord::Base
2
+ class << self
3
+ def find_or_create_with_name_like_and_kind(name, kind)
4
+ with_name_like_and_kind(name, kind).first || create!(:name => name, :kind => kind)
5
+ end
6
+ end
7
+
8
+ has_many :taggings, :dependent => :destroy
9
+
10
+ validates_presence_of :name
11
+ validates_uniqueness_of :name, :scope => :kind
12
+
13
+ scope :with_name_like_and_kind, lambda { |name, kind| { :conditions => ["name like ? AND kind = ?", name, kind] } }
14
+ scope :of_kind, lambda { |kind| { :conditions => {:kind => kind} } }
15
+ end
@@ -0,0 +1,5 @@
1
+ class Tagging < ActiveRecord::Base
2
+ belongs_to :tag
3
+ belongs_to :taggable, :polymorphic => true
4
+ belongs_to :owner, :polymorphic => true
5
+ end
@@ -0,0 +1,3 @@
1
+ module ActsAsOrganizable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails/generators'
2
+
3
+ class ActsAsOrganizable::CategorizableMigrationGenerator < Rails::Generators::Base
4
+ def self.source_root
5
+ # source root must return path to where templates are stored
6
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
7
+ end
8
+
9
+
10
+ def self.next_migration_number(path)
11
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
12
+ end
13
+
14
+ def create_migration_file
15
+ template 'categorizable_migration.rb', "db/migrate/#{Time.now.utc.strftime("%Y%m%d%H%M%S")}_create_categorizables.rb"
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails/generators'
2
+
3
+ class ActsAsOrganizable::TaggableMigrationGenerator < Rails::Generators::Base
4
+ def self.source_root
5
+ # source root must return path to where templates are stored
6
+ @source_root ||= File.join(File.dirname(__FILE__), '../templates')
7
+ end
8
+
9
+
10
+ def self.next_migration_number(path)
11
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
12
+ end
13
+
14
+ def create_migration_file
15
+ template 'create_taggables.rb', "db/migrate/#{Time.now.utc.strftime("%Y%m%d%H%M%S")}_create_taggables.rb"
16
+ end
17
+
18
+ end
@@ -0,0 +1,26 @@
1
+ class CreateTaggables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.string :name
5
+ t.string :kind
6
+ t.timestamps
7
+ end
8
+
9
+ create_table :taggings do |t|
10
+ t.integer :tag_id
11
+ t.references :taggable, :polymorphic => true
12
+ t.references :owner, :polymorphic => true
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :tags, [:name, :kind]
17
+ add_index :taggings, [:tag_id]
18
+ add_index :taggings, [:taggable_id, :taggable_type]
19
+ add_index :taggings, [:owner_id, :owner_type]
20
+ end
21
+
22
+ def self.down
23
+ drop_table :taggings
24
+ drop_table :tags
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acts_as_organizable
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Mike Hansen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-28 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description:
22
+ email:
23
+ - mhansen@pathlightmedia.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - Rakefile
34
+ - acts_as_organizable.gemspec
35
+ - init.rb
36
+ - lib/acts_as_organizable.rb
37
+ - lib/acts_as_organizable/core_ext/string.rb
38
+ - lib/acts_as_organizable/tag.rb
39
+ - lib/acts_as_organizable/tagging.rb
40
+ - lib/acts_as_organizable/version.rb
41
+ - lib/generators/acts_as_organizable/categorizable_migration_generator.rb
42
+ - lib/generators/acts_as_organizable/taggable_migration_generator.rb
43
+ - lib/generators/templates/create_taggables.rb
44
+ homepage:
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project: acts_as_organizable
73
+ rubygems_version: 1.8.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Simpler tagging.
77
+ test_files: []
78
+