tagger 0.1.0

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/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source :rubygems
2
+
3
+ gem "rails", "3.0.0"
4
+ gem "rspec-rails", "2.0.0.rc"
5
+ gem "activerecord", "3.0.0"
6
+ gem "sqlite3-ruby"
7
+
8
+ platforms :mri_19 do
9
+ gem "ruby-debug19", :require => "ruby-debug"
10
+ end
11
+
12
+ platforms :mri_18 do
13
+ gem "ruby-debug"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,110 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.0)
6
+ actionpack (= 3.0.0)
7
+ mail (~> 2.2.5)
8
+ actionpack (3.0.0)
9
+ activemodel (= 3.0.0)
10
+ activesupport (= 3.0.0)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.4.1)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.12)
16
+ rack-test (~> 0.5.4)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.0)
19
+ activesupport (= 3.0.0)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.4.1)
22
+ activerecord (3.0.0)
23
+ activemodel (= 3.0.0)
24
+ activesupport (= 3.0.0)
25
+ arel (~> 1.0.0)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.0)
28
+ activemodel (= 3.0.0)
29
+ activesupport (= 3.0.0)
30
+ activesupport (3.0.0)
31
+ archive-tar-minitar (0.5.2)
32
+ arel (1.0.1)
33
+ activesupport (~> 3.0.0)
34
+ builder (2.1.2)
35
+ columnize (0.3.1)
36
+ diff-lcs (1.1.2)
37
+ erubis (2.6.6)
38
+ abstract (>= 1.0.0)
39
+ i18n (0.4.1)
40
+ linecache (0.43)
41
+ linecache19 (0.5.11)
42
+ ruby_core_source (>= 0.1.4)
43
+ mail (2.2.5)
44
+ activesupport (>= 2.3.6)
45
+ mime-types
46
+ treetop (>= 1.4.5)
47
+ mime-types (1.16)
48
+ polyglot (0.3.1)
49
+ rack (1.2.1)
50
+ rack-mount (0.6.13)
51
+ rack (>= 1.0.0)
52
+ rack-test (0.5.4)
53
+ rack (>= 1.0)
54
+ rails (3.0.0)
55
+ actionmailer (= 3.0.0)
56
+ actionpack (= 3.0.0)
57
+ activerecord (= 3.0.0)
58
+ activeresource (= 3.0.0)
59
+ activesupport (= 3.0.0)
60
+ bundler (~> 1.0.0)
61
+ railties (= 3.0.0)
62
+ railties (3.0.0)
63
+ actionpack (= 3.0.0)
64
+ activesupport (= 3.0.0)
65
+ rake (>= 0.8.4)
66
+ thor (~> 0.14.0)
67
+ rake (0.8.7)
68
+ rspec (2.0.0.rc)
69
+ rspec-core (= 2.0.0.rc)
70
+ rspec-expectations (= 2.0.0.rc)
71
+ rspec-mocks (= 2.0.0.rc)
72
+ rspec-core (2.0.0.rc)
73
+ rspec-expectations (2.0.0.rc)
74
+ diff-lcs (>= 1.1.2)
75
+ rspec-mocks (2.0.0.rc)
76
+ rspec-core (= 2.0.0.rc)
77
+ rspec-expectations (= 2.0.0.rc)
78
+ rspec-rails (2.0.0.rc)
79
+ rspec (= 2.0.0.rc)
80
+ ruby-debug (0.10.3)
81
+ columnize (>= 0.1)
82
+ ruby-debug-base (~> 0.10.3.0)
83
+ ruby-debug-base (0.10.3)
84
+ linecache (>= 0.3)
85
+ ruby-debug-base19 (0.11.24)
86
+ columnize (>= 0.3.1)
87
+ linecache19 (>= 0.5.11)
88
+ ruby_core_source (>= 0.1.4)
89
+ ruby-debug19 (0.11.6)
90
+ columnize (>= 0.3.1)
91
+ linecache19 (>= 0.5.11)
92
+ ruby-debug-base19 (>= 0.11.19)
93
+ ruby_core_source (0.1.4)
94
+ archive-tar-minitar (>= 0.5.2)
95
+ sqlite3-ruby (1.3.1)
96
+ thor (0.14.0)
97
+ treetop (1.4.8)
98
+ polyglot (>= 0.3.1)
99
+ tzinfo (0.3.23)
100
+
101
+ PLATFORMS
102
+ ruby
103
+
104
+ DEPENDENCIES
105
+ activerecord (= 3.0.0)
106
+ rails (= 3.0.0)
107
+ rspec-rails (= 2.0.0.rc)
108
+ ruby-debug
109
+ ruby-debug19
110
+ sqlite3-ruby
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Nando Vieira
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,45 @@
1
+ = Tagger
2
+
3
+ == Installing
4
+
5
+ 1. Install the plugin with <tt>script/plugin install git://github.com/fnando/has_tags.git</tt>
6
+ 2. Generate the migration files with <tt>rails generate tagger:install</tt>.
7
+ 3. Finally, run the migrations with <tt>rake db:migrate</tt>.
8
+
9
+ == Usage
10
+
11
+ Add the method call <tt>taggable</tt> to your model.
12
+
13
+ class Link < ActiveRecord::Base
14
+ taggable :scope => :category
15
+ belongs_to :category
16
+ end
17
+
18
+ class Category < ActiveRecord::Base
19
+ has_many :links
20
+ end
21
+
22
+ @post = Post.first
23
+ @category = Category.first
24
+
25
+ # set tags using the chosen tag separator
26
+ @post.tagged_with = "rails, activerecord, models"
27
+
28
+ # get tags joined with the chosen tag separator
29
+ @post.tagged_with
30
+
31
+ # get all tags as objects
32
+ @post.tags
33
+
34
+ # check if an user tagged this object
35
+ @post.tag_owner?(current_user)
36
+
37
+ # generate a tag cloud
38
+ @tags = Tag.cloud(:link, :limit => 100, :scope => @category)
39
+
40
+ # at the view, call the helper tag_cloud
41
+ tag_cloud @tags do |tag_name, css|
42
+ <%= link_to tag_name, tag_path(tag_name), :class => css
43
+ end
44
+
45
+ Copyright (c) 2008 Nando Vieira, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require "rspec/core/rake_task"
2
+ require "./lib/tagger/version"
3
+
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.ruby_opts = %w[-Ilib -Ispec]
6
+ end
7
+
8
+ begin
9
+ require "jeweler"
10
+
11
+ JEWEL = Jeweler::Tasks.new do |gem|
12
+ gem.name = "tagger"
13
+ gem.version = Tagger::Version::STRING
14
+ gem.summary = "Add tagging support for Rails apps"
15
+ gem.description = "Add tagging support for Rails apps"
16
+ gem.authors = ["Nando Vieira"]
17
+ gem.email = "fnando.vieira@gmail.com"
18
+ gem.homepage = "http://github.com/fnando/tagger"
19
+ gem.has_rdoc = false
20
+ gem.add_dependency "rails", ">= 3.0.0"
21
+ gem.files = FileList["{Gemfile,Gemfile.lock,Rakefile,MIT-LICENSE,tagger.gemspec,README.rdoc}", "{lib,spec,templates}/**/*"]
22
+ end
23
+
24
+ Jeweler::GemcutterTasks.new
25
+ rescue LoadError => e
26
+ puts "You don't Jeweler installed, so you won't be able to build gems."
27
+ end
@@ -0,0 +1,78 @@
1
+ module Tagger
2
+ module ActiveRecord
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+
6
+ class << base
7
+ attr_accessor :taggable_options
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def taggable(options={})
13
+ include InstanceMethods
14
+
15
+ self.taggable_options = {
16
+ :separator => options[:separator],
17
+ :scope => options[:scope]
18
+ }
19
+
20
+ has_many :taggings, :as => :taggable, :dependent => :destroy
21
+ has_many :tags, :through => :taggings, :order => "tags.name asc"
22
+
23
+ after_save :save_tags
24
+ end
25
+ end
26
+
27
+ module InstanceMethods
28
+ def tagged_with=(tags)
29
+ @tagged_with = joined_tags(parsed_tags(tags))
30
+ end
31
+
32
+ def tagged_with
33
+ @tagged_with ||= joined_tags(tags.all.collect(&:name))
34
+ end
35
+
36
+ private
37
+ # Parse a tag string using the preferred tag separator.
38
+ #
39
+ # parsed_tags("a, d, c, b")
40
+ # #=> ["a", "b", "c", "d"]
41
+ #
42
+ def parsed_tags(tags)
43
+ Tag.parse(tags, self.class.taggable_options[:separator])
44
+ end
45
+
46
+ # Receive an array of tags and join them using the
47
+ # preferred tag separator.
48
+ #
49
+ # joined_tags %w[b c d a]
50
+ # #=> a, b, c, d
51
+ #
52
+ def joined_tags(tags)
53
+ tags = tags.sort_by {|tag| tag.downcase}
54
+
55
+ if self.class.taggable_options[:separator] == :space
56
+ tags.collect {|tag| tag.include?(" ") ? %["#{tag}"] : tag }.join(" ")
57
+ else
58
+ tags.join(", ")
59
+ end
60
+ end
61
+
62
+ # Remove all associated tags and re-create them.
63
+ def save_tags
64
+ Tag.transaction do
65
+ taggings.delete_all
66
+
67
+ scope = nil
68
+ scope = send(self.class.taggable_options[:scope]) if self.class.taggable_options[:scope]
69
+
70
+ parsed_tags(tagged_with).each do |tag_name|
71
+ tag = Tag.find_or_create_by_name(tag_name)
72
+ self.taggings.create(:tag => tag, :scope => scope)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,13 @@
1
+ require "rails/generators/base"
2
+
3
+ module Tagger
4
+ class InstallGenerator < ::Rails::Generators::Base
5
+ source_root File.dirname(__FILE__) + "/../../templates"
6
+
7
+ def copy_migrations
8
+ stamp = proc {|time| time.utc.strftime("%Y%m%d%H%M%S")}
9
+ copy_file "tags.rb", "db/migrations/#{stamp[Time.now]}_create_tags.rb"
10
+ copy_file "taggings.rb", "db/migrations/#{stamp[1.second.from_now]}_create_taggings.rb"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module Tagger
2
+ module Helper
3
+ # Build a tag cloud based on the provided tags.
4
+ #
5
+ # tag_cloud @tags do {|tag_name, css_class|}
6
+ # tag_cloud @tags, %w(t1 t2 t3 t4 t5) do {|tag_name, css_class| #=> do something }
7
+ #
8
+ def tag_cloud(tags, css_classes = nil, &block)
9
+ # set css classes if not specified
10
+ css_classes ||= %w[s1 s2 s3 s4 s5]
11
+
12
+ # collect all tag totals
13
+ totals = tags.collect(&:total)
14
+
15
+ # get max and min totals
16
+ max = totals.max.to_i
17
+ min = totals.min.to_i
18
+
19
+ divisor = ((max - min) / css_classes.size) + 1
20
+
21
+ tags.each do |tag|
22
+ yield tag.name, css_classes[(tag.total - min) / divisor] if divisor != 0
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module Tagger
2
+ class Railtie < Rails::Railtie
3
+ generators do
4
+ require "tagger/generator"
5
+ end
6
+
7
+ initializer "tagger.initializer" do |app|
8
+ ::ActiveRecord::Base.send :include, ActiveRecord
9
+ ::ActionView::Base.send :include, Helper
10
+ end
11
+ end
12
+ end
data/lib/tagger/tag.rb ADDED
@@ -0,0 +1,51 @@
1
+ class Tag < ActiveRecord::Base
2
+ SEPARATORS = {
3
+ :comma => ",",
4
+ :space => " "
5
+ }
6
+
7
+ has_many :taggings
8
+ attr_reader :weight
9
+
10
+ # Tag.cloud(:post)
11
+ # Tag.cloud(:post, :scope => @category)
12
+ # Tag.cloud(:post, :limit => 50)
13
+ def self.cloud(type, options = {})
14
+ options[:limit] ||= 100
15
+
16
+ scope_condition = "
17
+ AND scopable_type = #{options[:scope].class.to_s.inspect}
18
+ AND scopable_id = #{options[:scope].id}" if options[:scope]
19
+
20
+ tags = Tag.find_by_sql [%(
21
+ SELECT tags.id, tags.name, COUNT(*) AS total
22
+ FROM tags, taggings
23
+ WHERE
24
+ tags.id == taggings.tag_id AND
25
+ taggings.taggable_type = ?
26
+ #{scope_condition}
27
+ GROUP BY taggings.tag_id
28
+ ORDER BY total DESC, name ASC
29
+ LIMIT ?
30
+ ), type.to_s.classify, options[:limit]]
31
+ end
32
+
33
+ # Tag.parse(tags_list, :comma)
34
+ # Tag.parse(tags_list, :space)
35
+ def self.parse(tag_list, separator)
36
+ separator = SEPARATORS[separator] || SEPARATORS[:comma]
37
+ tags = []
38
+ tag_list = tag_list.dup
39
+ tag_list.gsub!(/(["'])(.*?)(\1)/ms) {|m| tags << $2; "" }
40
+ tags += tag_list.split(separator)
41
+ tags.collect(&:squish).reject(&:blank?).sort_by {|tag| tag.downcase}
42
+ end
43
+
44
+ def total
45
+ read_attribute(:total).to_i
46
+ end
47
+
48
+ def to_s
49
+ name
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ class Tagging < ActiveRecord::Base
2
+ belongs_to :tag
3
+ belongs_to :taggable, :polymorphic => true
4
+
5
+ before_save :define_scope
6
+
7
+ def scope=(object)
8
+ @scope = object
9
+ end
10
+
11
+ def scope
12
+ @scope = scopable_type.constantize.find(scopable_id) if !!scopable_id
13
+ end
14
+
15
+ private
16
+ def define_scope
17
+ unless @scope.blank?
18
+ write_attribute(:scopable_type, @scope.class.to_s)
19
+ write_attribute(:scopable_id, @scope.id)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ module Tagger
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 0
6
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
+ end
8
+ end
data/lib/tagger.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "rails/railtie"
2
+ require "active_record"
3
+
4
+ require "tagger/helper"
5
+ require "tagger/tag"
6
+ require "tagger/tagging"
7
+ require "tagger/active_record"
8
+ require "tagger/version"
9
+ require "tagger/railtie"
@@ -0,0 +1,5 @@
1
+ programming:
2
+ name: Programming
3
+
4
+ essays:
5
+ name: Essays
@@ -0,0 +1,3 @@
1
+ simples_ideias:
2
+ title: Simples Idéias
3
+ url: http://simplesideias.com.br
@@ -0,0 +1,7 @@
1
+ all_about_rails:
2
+ title: All About Rails
3
+ category: programming
4
+
5
+ ideas:
6
+ title: Ideas for Startups
7
+ category: essays
@@ -0,0 +1,2 @@
1
+ ruby:
2
+ name: ruby
@@ -0,0 +1,4 @@
1
+ require "spec_helper"
2
+
3
+ describe Tagger::Helper do
4
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,23 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :categories do |t|
3
+ t.string :name
4
+ end
5
+
6
+ create_table :posts do |t|
7
+ t.references :category
8
+ t.string :title
9
+ end
10
+
11
+ create_table :links do |t|
12
+ t.string :title, :url
13
+ end
14
+
15
+ create_table :taggings do |t|
16
+ t.references :tag, :user
17
+ t.references :scopable, :taggable, :polymorphic => true
18
+ end
19
+
20
+ create_table :tags do |t|
21
+ t.string :name
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require "rails"
3
+ require "tagger"
4
+ require File.dirname(__FILE__) + "/support/config/boot"
5
+ require "rspec/rails"
6
+
7
+ require "support/models"
8
+
9
+ # Load database schema
10
+ load File.dirname(__FILE__) + "/schema.rb"
11
+
12
+ RSpec.configure do |config|
13
+ config.mock_with :rspec
14
+ config.fixture_path = File.dirname(__FILE__) + "/fixtures"
15
+ config.use_transactional_fixtures = true
16
+ end
@@ -0,0 +1,14 @@
1
+ ENV["BUNDLE_GEMFILE"] = File.dirname(__FILE__) + "/../../../Gemfile"
2
+ require "bundler"
3
+ Bundler.setup
4
+ require "rails/all"
5
+ Bundler.require(:default)
6
+
7
+ module Tagger
8
+ class Application < Rails::Application
9
+ config.root = File.dirname(__FILE__) + "/.."
10
+ config.active_support.deprecation = :log
11
+ end
12
+ end
13
+
14
+ Tagger::Application.initialize!
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"