Permalink
Browse files

put taggable code in place with specs

  • Loading branch information...
1 parent 6937472 commit ff49e988dcc561b02c17b7931335772ef3d15449 @jackdempsey committed Dec 3, 2008
Showing with 145 additions and 33 deletions.
  1. +81 −10 lib/sequel_taggable/sequel_taggable.rb
  2. +1 −3 lib/sequel_taggable/tag.rb
  3. +63 −20 spec/sequel_taggable/sequel_taggable_spec.rb
@@ -3,20 +3,91 @@ module Plugins
module Taggable
# Apply the plugin to the model.
def self.apply(model, options = {})
- model.class_eval do
- attr_accessor :tags
- end
end
module InstanceMethods
- # Define methods that will be instance-specific here.
- def <<
+ def taggable?
+ self.class.taggable?
end
end
-
- module ClassMethods
+ module SingletonMethods
+ # Class Methods
+ def tagged_with(string, options = {})
+ tag = Tag.first(:name => string)
+ conditions = {}
+ conditions[:tag_id] = tag.id
+ conditions[:tag_context] = options[:on] if options[:on]
+ Tagging.all(conditions).map{|tagging| tagging.taggable}
+ end
+
+ def taggable?
+ true
+ end
end
- end
- end
-end
+
+ module ClassMethods
+ def self.extended(klass)
+ klass.has_tags
+ end
+
+ def has_tags
+ has_tags_on :tags
+ end
+
+ def taggable?
+ false
+ end
+
+ def has_tags_on(*args)
+ args.flatten!
+ args.uniq!
+
+ self.extend(Sequel::Plugins::Taggable::SingletonMethods)
+
+ args.map{|arg| arg.to_sym}.each do |arg|
+ arg_singular = arg.to_s.singularize
+ class_eval <<-RUBY
+ attr_accessor :frozen_#{arg_singular}_list
+ one_to_many :#{arg_singular}_taggings, :class_name => "Tagging", :child_key => [:taggable_id],
+ :taggable_type => self.to_s, :tag_context => "#{arg}"
+ before_save :update_#{arg}
+
+ def #{arg}
+ #{arg_singular}_taggings.map{|tagging| tagging.tag}
+ end
+
+ def #{arg_singular}_list
+ @#{arg_singular}_list || #{arg}.map{|#{arg_singular}| #{arg_singular}.name}.sort
+ end
+
+ def #{arg_singular}_list=(string)
+ @#{arg_singular}_list = string.to_s.split(",").map{|name| name.gsub(/[^\\w\\s_-]/i,"").strip}.uniq.sort
+ end
+
+ def update_#{arg}
+ return if #{arg_singular}_list.empty?
+ deleted_#{arg} = frozen_#{arg_singular}_list.to_s.split(',') - #{arg_singular}_list
+ deleted_#{arg}.each do |name|
+ tag = Tag.first(:name => name)
+ tagging = #{arg_singular}_taggings.first(:tag_id => tag.id)
+ tagging.destroy
+ #{arg_singular}_taggings.reload
+ end
+ #{arg_singular}_list.each do |name|
+ tag = Tag.first(:name => name)
+ next if #{arg}.to_a.include?(tag)
+ tag = Tag.create!(:name => name) unless tag
+ #{arg_singular}_taggings << Tagging.new(:tag => tag, :taggable_type => self.class.to_s, :tag_context => "#{arg}")
+ end
+ self.frozen_#{arg_singular}_list = #{arg}.map{|#{arg_singular}| #{arg_singular}.name}.sort.join(',')
+ end
+ RUBY
+
+ end
+ end
+ end # ClassMethods
+ end # Taggable
+ end # Plugins
+end # Sequel
+
@@ -2,7 +2,5 @@ class Tag < Sequel::Model
one_to_many :taggings
validates_presence_of :name
- # def taggables
- # taggings.map{|tagging| tagging.taggable}
- # end
+
end
@@ -5,24 +5,67 @@
@example = ExampleModel.new
end
- it "should give models a #tags method" do
- @example.should respond_to(:tags)
+ it "should add a .has_tags method to models which include DataMapper::Resource" do
+ ExampleModel.should respond_to(:has_tags)
+ # AnotherTaggedModel.should respond_to(:has_tags)
+ # DefaultTaggedModel.should respond_to(:has_tags)
+ # UntaggedModel.should respond_to(:has_tags)
end
-
- describe
- it "should give models a #tags method" do
- @example.should respond_to(:tags)
- end
-
- it "should give models a #tags= method" do
- @example.should respond_to(:tags=)
- end
-
- it "should give models a #<< method" do
- @example.should respond_to(:<<)
- end
-
- it "should save a models tags" do
-
- end
-end
+ #
+ # it "should add a .has_tags_on method to models which include DataMapper::Resource" do
+ # TaggedModel.should respond_to(:has_tags_on)
+ # AnotherTaggedModel.should respond_to(:has_tags_on)
+ # DefaultTaggedModel.should respond_to(:has_tags_on)
+ # UntaggedModel.should respond_to(:has_tags_on)
+ # end
+ #
+ # describe ".has_tags_on" do
+ # it "should accept an array of context names" do
+ # class HasTagsOnTestModel
+ # include DataMapper::Resource
+ # property :id, Integer, :serial => true
+ # end
+ # lambda{HasTagsOnTestModel.has_tags_on(:should, 'not', :raise)}.should_not raise_error(ArgumentError)
+ # end
+ #
+ # it "should create taggable functionality for each of the context names passed" do
+ # class TestModel
+ # include DataMapper::Resource
+ # property :id, Integer, :serial => true
+ #
+ # has_tags_on(:pets, 'skills', :tags)
+ # end
+ # TestModel.should be_taggable
+ # a = TestModel.new
+ # a.should be_taggable
+ # a.should respond_to(:pet_list)
+ # a.should respond_to(:skill_list)
+ # a.should respond_to(:tag_list)
+ # a.should respond_to(:pet_list=)
+ # a.should respond_to(:skill_list=)
+ # a.should respond_to(:tag_list=)
+ # end
+ # end
+ #
+ # describe ".has_tags" do
+ # it "should create a taggable with 'tags' context regardless of passed arguments" do
+ # class TagsOnly
+ # include DataMapper::Resource
+ # property :id, Integer, :serial => true
+ # has_tags :pets, :skills
+ # end
+ # TagsOnly.should be_taggable
+ # TagsOnly.new.should be_taggable
+ # a = TagsOnly.new
+ # a.should respond_to(:tag_list)
+ # a.should respond_to(:tag_list=)
+ # a.should respond_to(:tags)
+ # a.should_not respond_to(:pet_list)
+ # a.should_not respond_to(:pet_list=)
+ # a.should_not respond_to(:pets)
+ # a.should_not respond_to(:skill_list)
+ # a.should_not respond_to(:skill_list=)
+ # a.should_not respond_to(:skills)
+ # end
+ # end
+end

0 comments on commit ff49e98

Please sign in to comment.