acts_as_organizable 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+