├── .rvmrc.example ├── lib ├── make_flaggable │ ├── version.rb │ ├── flaggable.rb │ ├── flagging.rb │ ├── exceptions.rb │ └── flagger.rb ├── generators │ └── make_flaggable │ │ ├── make_flaggable_generator.rb │ │ └── templates │ │ └── migration.rb └── make_flaggable.rb ├── spec ├── database.yml ├── database.yml.sample ├── models │ └── flagging_spec.rb ├── models.rb ├── support │ └── be_accessible_matcher.rb ├── generators │ └── make_flaggable_generator_spec.rb ├── schema.rb ├── spec_helper.rb └── lib │ └── make_flaggable_spec.rb ├── .gitignore ├── Gemfile ├── .travis.yml ├── gemfiles ├── Gemfile.rails-3.0.x └── Gemfile.rails-3.1.x ├── Rakefile ├── MIT-LICENSE ├── make_flaggable.gemspec └── README.rdoc /.rvmrc.example: -------------------------------------------------------------------------------- 1 | rvm use 1.9.2-p290@make_flaggable --create -------------------------------------------------------------------------------- /lib/make_flaggable/version.rb: -------------------------------------------------------------------------------- 1 | module MakeFlaggable 2 | VERSION = "0.1.1" 3 | end 4 | -------------------------------------------------------------------------------- /spec/database.yml: -------------------------------------------------------------------------------- 1 | sqlite3: 2 | adapter: sqlite3 3 | database: make_flaggable.sqlite3 4 | -------------------------------------------------------------------------------- /spec/database.yml.sample: -------------------------------------------------------------------------------- 1 | sqlite3: 2 | adapter: sqlite3 3 | database: make_flaggable.sqlite3 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | pkg/* 3 | doc/* 4 | *.gem 5 | *.log 6 | *.sqlite3 7 | .bundle 8 | .rvmrc 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | # Specify your gem's dependencies in make_flaggable.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.2 4 | - 1.9.3 5 | gemfile: 6 | - gemfiles/Gemfile.rails-3.0.x 7 | - gemfiles/Gemfile.rails-3.1.x -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-3.0.x: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem "activerecord", ">= 3.0.0" 4 | gem "bundler", ">= 1.0.0" 5 | gem "rspec", ">= 2.6.0" 6 | gem "database_cleaner", "0.6.7" 7 | gem "sqlite3", ">= 1.3.0" 8 | gem "generator_spec", ">= 0.8.2" 9 | gem "rake", "0.8.7" 10 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-3.1.x: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem "activerecord", ">= 3.1.0" 4 | gem "bundler", ">= 1.0.0" 5 | gem "rspec", ">= 2.6.0" 6 | gem "database_cleaner", "0.6.7" 7 | gem "sqlite3", ">= 1.3.0" 8 | gem "generator_spec", ">= 0.8.4" 9 | gem "rake", "0.8.7" 10 | -------------------------------------------------------------------------------- /spec/models/flagging_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Flagging do 4 | describe "attributes should be white listed" do 5 | it { should allow_mass_assignment_of(:flaggable) } 6 | it { should allow_mass_assignment_of(:flagger) } 7 | it { should allow_mass_assignment_of(:flag) } 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rspec/core/rake_task' 5 | RSpec::Core::RakeTask.new(:spec) 6 | 7 | # Allows for 'rake' and 'rake spec' to execute specs. Fixes bug with message: 8 | # undefined method `shellescape' ... 9 | require 'shellwords' 10 | 11 | task :default => :spec 12 | -------------------------------------------------------------------------------- /spec/models.rb: -------------------------------------------------------------------------------- 1 | class Flagging < ActiveRecord::Base 2 | attr_accessible :flaggable, :flagger, :flag 3 | end 4 | 5 | class FlaggableModel < ActiveRecord::Base 6 | make_flaggable :favorite, :inappropriate 7 | end 8 | 9 | class FlaggerModel < ActiveRecord::Base 10 | make_flagger 11 | end 12 | 13 | class InvalidFlaggableModel < ActiveRecord::Base 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/be_accessible_matcher.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :allow_mass_assignment_of do |attribute| 2 | match do |response| 3 | response.class.accessible_attributes.include?(attribute) 4 | end 5 | description { "be accessible :#{attribute}" } 6 | failure_message_for_should { ":#{attribute} should be accessible" } 7 | failure_message_for_should_not { ":#{attribute} should not be accessible" } 8 | end 9 | -------------------------------------------------------------------------------- /lib/generators/make_flaggable/make_flaggable_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/migration' 2 | require 'rails/generators/active_record' 3 | 4 | class MakeFlaggableGenerator < Rails::Generators::Base 5 | include Rails::Generators::Migration 6 | 7 | desc "Generates a migration for the Flag model" 8 | 9 | def self.source_root 10 | @source_root ||= File.dirname(__FILE__) + '/templates' 11 | end 12 | 13 | def self.next_migration_number(path) 14 | ActiveRecord::Generators::Base.next_migration_number(path) 15 | end 16 | 17 | def generate_migration 18 | migration_template 'migration.rb', 'db/migrate/create_make_flaggable_tables' 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/make_flaggable/flaggable.rb: -------------------------------------------------------------------------------- 1 | module MakeFlaggable 2 | module Flaggable 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | has_many :flaggings, :class_name => "MakeFlaggable::Flagging", :as => :flaggable, :dependent => :destroy 7 | end 8 | 9 | module ClassMethods 10 | def flaggable? 11 | true 12 | end 13 | end 14 | 15 | def flagged?(flag = nil) 16 | if flag.nil? 17 | flaggings.count > 0 18 | else 19 | flaggings.where(:flag => flag.to_s).count > 0 20 | end 21 | end 22 | 23 | def flagged_by?(flagger, flag) 24 | flagger.flagged?(self, flag) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/make_flaggable/flagging.rb: -------------------------------------------------------------------------------- 1 | module MakeFlaggable 2 | class Flagging < ActiveRecord::Base 3 | belongs_to :flaggable, :polymorphic => true 4 | belongs_to :flagger, :polymorphic => true 5 | scope :with_flag, lambda { |flag| where(:flag => flag.to_s) } 6 | scope :with_flaggable, lambda { |flaggable| where(:flaggable_type => flaggable.class.name, :flaggable_id => flaggable.id) } 7 | 8 | def flaggable 9 | @flaggable 10 | end 11 | 12 | def flaggable=(value) 13 | @flaggable = value 14 | end 15 | 16 | def flagger 17 | @flagger 18 | end 19 | 20 | def flagger=(value) 21 | @flagger = value 22 | end 23 | 24 | def flag 25 | @flag 26 | end 27 | 28 | def flag=(value) 29 | @flag = value 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/generators/make_flaggable_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'action_controller' 3 | require 'generator_spec/test_case' 4 | require 'generators/make_flaggable/make_flaggable_generator' 5 | 6 | describe MakeFlaggableGenerator do 7 | include GeneratorSpec::TestCase 8 | destination File.expand_path("/tmp", __FILE__) 9 | tests MakeFlaggableGenerator 10 | 11 | before do 12 | prepare_destination 13 | run_generator 14 | end 15 | 16 | specify do 17 | destination_root.should have_structure { 18 | directory "db" do 19 | directory "migrate" do 20 | migration "create_make_flaggable_tables" do 21 | contains "class CreateMakeFlaggableTables" 22 | contains "create_table :flaggings" 23 | end 24 | end 25 | end 26 | } 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/make_flaggable/exceptions.rb: -------------------------------------------------------------------------------- 1 | module MakeFlaggable 2 | module Exceptions 3 | class AlreadyFlaggedError < StandardError 4 | def initialize 5 | super "The flaggable was already flagged by this flagger." 6 | end 7 | end 8 | 9 | class NotFlaggedError < StandardError 10 | def initialize 11 | super "The flaggable was not flagged by the flagger." 12 | end 13 | end 14 | 15 | class InvalidFlaggableError < StandardError 16 | def initialize 17 | super "Invalid flaggable." 18 | end 19 | end 20 | 21 | class InvalidFlagError < StandardError 22 | def initialize 23 | super "Invalid flag." 24 | end 25 | end 26 | 27 | class MissingFlagsError < StandardError 28 | def initialize 29 | super "Missing options :flags for make_flaggable" 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/schema.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Schema.define :version => 0 do 2 | create_table :flaggable_models, :force => true do |t| 3 | t.string :name 4 | end 5 | 6 | create_table :flagger_models, :force => true do |t| 7 | t.string :name 8 | end 9 | 10 | create_table :invalid_flaggable_models, :force => true do |t| 11 | t.string :name 12 | end 13 | 14 | create_table :flaggings, :force => true do |t| 15 | t.string :flaggable_type 16 | t.integer :flaggable_id 17 | t.string :flagger_type 18 | t.integer :flagger_id 19 | t.string :flag 20 | 21 | t.timestamps 22 | end 23 | 24 | add_index :flaggings, [:flaggable_type, :flaggable_id] 25 | add_index :flaggings, [:flag, :flaggable_type, :flaggable_id] 26 | add_index :flaggings, [:flagger_type, :flagger_id, :flaggable_type, :flaggable_id], :name => "access_flaggings" 27 | add_index :flaggings, [:flag, :flagger_type, :flagger_id, :flaggable_type, :flaggable_id], :name => "access_flag_flaggings" 28 | end 29 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2011 by Kai Schlamp 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. -------------------------------------------------------------------------------- /lib/generators/make_flaggable/templates/migration.rb: -------------------------------------------------------------------------------- 1 | class CreateMakeFlaggableTables < ActiveRecord::Migration 2 | def self.up 3 | create_table :flaggings do |t| 4 | t.string :flaggable_type 5 | t.integer :flaggable_id 6 | t.string :flagger_type 7 | t.integer :flagger_id 8 | t.string :flag 9 | 10 | t.timestamps 11 | end 12 | 13 | add_index :flaggings, [:flaggable_type, :flaggable_id] 14 | add_index :flaggings, [:flag, :flaggable_type, :flaggable_id] 15 | add_index :flaggings, [:flagger_type, :flagger_id, :flaggable_type, :flaggable_id], :name => "access_flaggings" 16 | add_index :flaggings, [:flag, :flagger_type, :flagger_id, :flaggable_type, :flaggable_id], :name => "access_flag_flaggings" 17 | end 18 | 19 | def self.down 20 | remove_index :flaggings, :column => [:flaggable_type, :flaggable_id] 21 | remove_index :flaggings, :column => [:flag, :flaggable_type, :flaggable_id] 22 | remove_index :flaggings, :name => "access_flaggings" 23 | remove_index :flaggings, :name => "access_flag_flaggings" 24 | 25 | drop_table :flaggings 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | require 'logger' 4 | require 'rspec' 5 | require 'active_record' 6 | require 'database_cleaner' 7 | require 'support/be_accessible_matcher' 8 | 9 | $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') 10 | require 'make_flaggable' 11 | 12 | ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log') 13 | ActiveRecord::Base.configurations = YAML::load_file(File.dirname(__FILE__) + '/database.yml') 14 | ActiveRecord::Base.establish_connection(ENV['DB'] || 'sqlite3') 15 | 16 | ActiveRecord::Base.silence do 17 | ActiveRecord::Migration.verbose = false 18 | 19 | load(File.dirname(__FILE__) + '/schema.rb') 20 | load(File.dirname(__FILE__) + '/models.rb') 21 | end 22 | 23 | RSpec.configure do |config| 24 | config.filter_run :focus => true 25 | config.run_all_when_everything_filtered = true 26 | config.filter_run_excluding :exclude => true 27 | 28 | config.mock_with :rspec 29 | 30 | config.before(:suite) do 31 | DatabaseCleaner.strategy = :truncation 32 | DatabaseCleaner.clean 33 | end 34 | 35 | config.after(:each) do 36 | DatabaseCleaner.clean 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/make_flaggable.rb: -------------------------------------------------------------------------------- 1 | require 'make_flaggable/flagging' 2 | require 'make_flaggable/flaggable' 3 | require 'make_flaggable/flagger' 4 | require 'make_flaggable/exceptions' 5 | 6 | module MakeFlaggable 7 | def flaggable? 8 | false 9 | end 10 | 11 | def flagger? 12 | false 13 | end 14 | 15 | # Specify a model as flaggable. 16 | # Required options flag names to allow flagging of with those flag types 17 | # 18 | # Example: 19 | # class Article < ActiveRecord::Base 20 | # make_flaggable :inappropriate, :spam, :favorite 21 | # end 22 | def make_flaggable(*flags) 23 | raise MakeFlaggable::Exceptions::MissingFlagsError.new if flags.empty? 24 | @flags = flags.map!(&:to_sym) 25 | # Add available_flags as an instance method 26 | define_method(:available_flags) { flags } 27 | # Add available_flags as a class method 28 | instance_eval { def available_flags; @flags; end } 29 | include Flaggable 30 | end 31 | 32 | # Specify a model as flagger. 33 | # 34 | # Example: 35 | # class User < ActiveRecord::Base 36 | # make_flagger 37 | # end 38 | def make_flagger 39 | include Flagger 40 | end 41 | end 42 | 43 | ActiveRecord::Base.extend MakeFlaggable 44 | -------------------------------------------------------------------------------- /make_flaggable.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path("../lib/make_flaggable/version", __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "make_flaggable" 6 | s.version = MakeFlaggable::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ["Kai Schlamp", "Eric Berry"] 9 | s.email = ["schlamp@gmx.de", "cavneb@gmail.com"] 10 | s.homepage = "http://github.com/cavneb/make_flaggable" 11 | s.summary = "Rails 3 flagging extension with named flags" 12 | s.description = "A user-centric flagging extension for Rails 3 applications." 13 | 14 | s.required_rubygems_version = ">= 1.3.6" 15 | s.rubyforge_project = "make_flaggable" 16 | 17 | s.add_dependency "activerecord", ">= 3.0.0" 18 | s.add_development_dependency "bundler", ">= 1.0.0" 19 | s.add_development_dependency "rspec", ">= 2.6.0" 20 | s.add_development_dependency "database_cleaner", ">= 0.6.7" 21 | s.add_development_dependency "sqlite3", ">= 1.3.0" 22 | s.add_development_dependency "generator_spec", ">= 0.8.4" 23 | s.add_development_dependency "rake", "0.8.7" 24 | 25 | s.files = `git ls-files`.split("\n") 26 | s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact 27 | s.require_path = 'lib' 28 | end 29 | -------------------------------------------------------------------------------- /lib/make_flaggable/flagger.rb: -------------------------------------------------------------------------------- 1 | module MakeFlaggable 2 | module Flagger 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | has_many :flaggings, :class_name => "MakeFlaggable::Flagging", :as => :flagger, :dependent => :destroy 7 | end 8 | 9 | module ClassMethods 10 | def flagger? 11 | true 12 | end 13 | end 14 | 15 | # Flag a +flaggable+ using the provided +flag+. 16 | # Raises an +AlreadyFlaggedError+ if the flagger already flagged the flaggable with the same +:flag+. 17 | # Raises an +InvalidFlaggableError+ if the flaggable is not a valid flaggable. 18 | # Raises an +InvalidFlagError+ if the flaggable does not allow the provided +:flag+ as a flag value. 19 | def flag!(flaggable, flag) 20 | check_flaggable(flaggable, flag) 21 | 22 | if flagged?(flaggable, flag) 23 | raise MakeFlaggable::Exceptions::AlreadyFlaggedError.new 24 | end 25 | 26 | Flagging.create(:flaggable => flaggable, :flagger => self, :flag => flag) 27 | end 28 | 29 | # Flag the +flaggable+, but don't raise an error if the flaggable was already flagged by the +flagger+ with the +:flag+. 30 | def flag(flaggable, flag) 31 | begin 32 | flag!(flaggable, flag) 33 | rescue Exceptions::AlreadyFlaggedError 34 | end 35 | end 36 | 37 | def unflag!(flaggable, flag) 38 | check_flaggable(flaggable, flag) 39 | 40 | flaggings = fetch_flaggings(flaggable, flag) 41 | 42 | raise Exceptions::NotFlaggedError if flaggings.empty? 43 | 44 | flaggings.destroy_all 45 | 46 | true 47 | end 48 | 49 | def unflag(flaggable, flag) 50 | begin 51 | unflag!(flaggable, flag) 52 | success = true 53 | rescue Exceptions::NotFlaggedError 54 | success = false 55 | end 56 | success 57 | end 58 | 59 | # Toggles the state of a given flag on a flaggable object 60 | # and returns the new flag state 61 | def toggle_flag(flaggable, flag) 62 | flagged?(flaggable, flag) ? unflag(flaggable, flag) : flag(flaggable, flag) 63 | flagged?(flaggable, flag) 64 | end 65 | 66 | def flagged?(flaggable, flag = nil) 67 | check_flaggable(flaggable, flag) 68 | fetch_flaggings(flaggable, flag).try(:first) ? true : false 69 | end 70 | 71 | # Returns the most recently created flag 72 | def find_last_flag_for(flaggable, flag=nil) 73 | find_all_flags_for(flaggable, flag).first 74 | end 75 | 76 | # Returns all flags created by the user for a given flag 77 | def find_all_flags_for(flaggable, flag=nil) 78 | fetch_flaggings(flaggable, flag).order('created_at desc') 79 | end 80 | 81 | private 82 | 83 | def fetch_flaggings(flaggable, flag) 84 | conditions = { :flaggable_type => flaggable.class.to_s, :flaggable_id => flaggable.id } 85 | conditions.merge!(:flag => flag.to_s) if flag.present? 86 | flaggings.where(conditions) 87 | end 88 | 89 | def check_flaggable(flaggable, flag) 90 | raise Exceptions::InvalidFlaggableError unless flaggable.class.flaggable? 91 | 92 | if flag.present? 93 | raise Exceptions::InvalidFlagError unless flaggable.available_flags.include? flag.to_sym 94 | end 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = MakeFlaggable 2 | 3 | {Build Status}[https://travis-ci.org/cavneb/make_flaggable] 4 | 5 | = NOT MAINTAINED. I RECOMMEND [https://github.com/dgilperez/make_flaggable] 6 | 7 | MakeFlaggable is an extension for building a user-centric flagging system for Rails 3 applications. 8 | It currently supports ActiveRecord models. 9 | 10 | Special thanks to medihack[https://github.com/medihack/make_flaggable] for providing this awesome gem! 11 | 12 | == Installation 13 | 14 | add MakeFlaggable to your Gemfile 15 | 16 | gem 'make_flaggable', :git => 'git://github.com/cavneb/make_flaggable.git' 17 | 18 | afterwards execute 19 | 20 | bundle install 21 | 22 | generate the required migration file 23 | 24 | rails generate make_flaggable 25 | 26 | migrate the database 27 | 28 | rake db:migrate 29 | 30 | == Usage 31 | 32 | # Specify a model that can be flagged and provide the names of the flags 33 | class Article < ActiveRecord::Base 34 | make_flaggable :inappropriate, :favorite 35 | end 36 | 37 | # Specify a model that can flag another model. 38 | class User < ActiveRecord::Base 39 | make_flagger 40 | end 41 | 42 | # The user can now flag the flaggable. 43 | # If the user already flagged the flaggable with the :flag_name then an AlreadyFlaggedError is raised. 44 | user.flag!(article, :flag_name) 45 | 46 | # The method without bang(!) does not raise the AlreadyFlaggedError when the user flags the flaggable more than once. 47 | # Instead it just returns false and ignores the flagging. 48 | user.flag(article, :flag_name) 49 | 50 | # The user may unflag an already done flagging. 51 | # If the user never flagged the flaggable then an NotFlaggedError is raised. 52 | user.unflag!(article, :flag_name) 53 | 54 | # The method without bang(!) does not raise the NotFlaggedError, but just returns false if the user never flagged 55 | # the flaggable. 56 | user.unflag(article, :flag_name) 57 | 58 | # The user can also easily toggle the state of a flag: subsquent calls to toggle_flag will flag/unflag it: 59 | user.toggle_flag(article, :flag_name) # returns true as the flag has been set 60 | user.toggle_flag(article, :flag_name) # returns false as the flag has now been removed 61 | user.toggle_flag(article, :flag_name) # flag set again 62 | 63 | # Get all flaggings of a flaggable. 64 | article.flaggings 65 | 66 | # Get the flagging with a specified flag. 67 | article.flaggings.with_flag(:flag_name) 68 | 69 | # Get the flagger of the flagging. 70 | flagging = article.flaggings.with_flag(:flag_name).first 71 | user = flagging.flagger 72 | 73 | # Returns true if the flagger flagged the flaggable, false otherwise. 74 | user.flagged?(article, :flag_name) 75 | 76 | # Returns true if the flagger flagged the flaggable with any flag, false otherwise. 77 | # A more concise version of user.flaggings.with_flaggable(article) 78 | user.flagged?(article) 79 | 80 | # Return true if the flaggable was flagged by the flagger, false otherwise. 81 | article.flagged_by?(user, :flag_name) 82 | 83 | # Returns true if the article was flagged by any flagger at all, false otherwise. 84 | article.flagged?(:flag_name) 85 | 86 | # Flaggings can also be accessed by its flagger. 87 | flagger.flaggings or flagger.flaggings.with_flag(:flag_name) or flagger.flaggings.with_flaggable(:flaggable) 88 | 89 | # Get the available flags for a flaggable 90 | article.available_flags 91 | 92 | # Get the available flags for a flaggable class 93 | Article.available_flags 94 | 95 | # Get the last Flagging made on an article (Flaggable) by a user (Flagger) 96 | # Format: 97 | # # of any type 98 | # flagger.find_last_flag_for(flaggable) 99 | user.find_last_flag_for(article) 100 | 101 | # # of a specific type 102 | # flagger.find_last_flag_for(flaggable, flag) 103 | user.find_last_flag_for(article, :inappropriate) 104 | 105 | # Get all Flaggings made on an article (Flaggable) by a user (Flagger) 106 | # Format: 107 | # # of any type 108 | # flagger.find_all_flags_for(flaggable) 109 | user.find_all_flags_for(article) 110 | 111 | # # of a specific type 112 | # flagger.find_all_flags_for(flaggable, flag) 113 | user.find_all_flags_for(article, :spam) 114 | 115 | == Testing 116 | 117 | MakeFlaggable uses RSpec for testing and has a rake task for executing the provided specs 118 | 119 | rake spec 120 | 121 | or simply 122 | 123 | rake 124 | 125 | Copyright © 2010-2011 Kai Schlamp (http://www.medihack.org), released under the MIT license 126 | 127 | Modified to use specified flags by Eric Berry 128 | -------------------------------------------------------------------------------- /spec/lib/make_flaggable_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | describe "Make Flaggable" do 4 | before(:each) do 5 | @flaggable = FlaggableModel.create(:name => "Flaggable 1") 6 | @flaggable2 = FlaggableModel.create(:name => "Flaggable 2") 7 | 8 | @flagger = FlaggerModel.create(:name => "Flagger 1") 9 | @flagger2 = FlaggerModel.create(:name => "Flagger 2") 10 | end 11 | 12 | it "should create a flaggable instance" do 13 | @flaggable.class.should == FlaggableModel 14 | @flaggable.class.flaggable?.should == true 15 | end 16 | 17 | it "should create a flagger instance" do 18 | @flagger.class.should == FlaggerModel 19 | @flagger.class.flagger?.should == true 20 | end 21 | 22 | it "should have flaggings" do 23 | @flagger.flaggings.length.should == 0 24 | @flagger.flag!(@flaggable, :inappropriate) 25 | @flagger.flaggings.reload.length.should == 1 26 | end 27 | 28 | it "should have flaggings by flag name" do 29 | @flagger.flag!(@flaggable, :inappropriate) 30 | @flagger.flag!(@flaggable, :favorite) 31 | @flagger.flaggings.reload.length.should == 2 32 | @flagger.flaggings.with_flag(:favorite).length.should == 1 33 | end 34 | 35 | it "should have flaggings by flaggable" do 36 | @flagger.flag!(@flaggable, :inappropriate) 37 | @flagger.flag!(@flaggable, :favorite) 38 | @flagger.flag!(@flaggable2, :favorite) 39 | @flagger.flaggings.reload.length.should == 3 40 | @flagger.flaggings.with_flaggable(@flaggable).length.should == 2 41 | @flagger.flaggings.with_flaggable(@flaggable2).length.should == 1 42 | end 43 | 44 | describe "flagger" do 45 | describe "flag" do 46 | it "should create a flagging" do 47 | @flaggable.flaggings.length.should == 0 48 | @flagger.flag!(@flaggable, :inappropriate) 49 | @flaggable.flaggings.reload.length.should == 1 50 | end 51 | 52 | it "should not allow duplicate flaggings with the same flag and flaggable and throw exception" do 53 | @flaggable.flaggings.length.should == 0 54 | @flagger.flag!(@flaggable, :inappropriate) 55 | lambda { 56 | @flagger.flag!(@flaggable, :inappropriate) 57 | }.should raise_error(MakeFlaggable::Exceptions::AlreadyFlaggedError) 58 | end 59 | 60 | it "should not allow duplicate flaggings with the same flag and flaggable and return false" do 61 | @flaggable.flaggings.length.should == 0 62 | @flagger.flag!(@flaggable, :inappropriate) 63 | @flagger.flag(@flaggable, :inappropriate).should == nil 64 | end 65 | 66 | it "should remove related flaggings when destroyed" do 67 | @flagger.flag!(@flaggable, :inappropriate) 68 | @flagger2.flag!(@flaggable, :inappropriate) 69 | @flaggable.flaggings.count.should == 2 70 | 71 | @flagger.destroy 72 | @flaggable.flaggings.reload.count.should == 1 73 | @flaggable.flaggings.first.flagger.should == @flagger2 74 | 75 | @flagger2.destroy 76 | @flaggable.flaggings.reload.should be_empty 77 | end 78 | end 79 | 80 | describe "unflag" do 81 | it "should unflag a flagging" do 82 | @flagger.flag!(@flaggable, :inappropriate) 83 | @flagger.flaggings.length.should == 1 84 | @flagger.unflag!(@flaggable, :inappropriate).should == true 85 | @flagger.flaggings.reload.length.should == 0 86 | end 87 | 88 | it "should unflag individual flaggings based on the flag" do 89 | @flagger.flag!(@flaggable, :inappropriate) 90 | @flagger.flag!(@flaggable, :favorite) 91 | @flagger.flaggings.length.should == 2 92 | @flagger.unflag!(@flaggable, :inappropriate).should == true 93 | @flagger.flaggings.reload.length.should == 1 94 | end 95 | 96 | it "normal method should return true when successfully unflagged" do 97 | @flagger.flag(@flaggable, :favorite) 98 | @flagger.unflag(@flaggable, :favorite).should == true 99 | end 100 | 101 | it "should raise error if flagger not flagged the flaggable with bang method" do 102 | lambda { @flagger.unflag!(@flaggable, :favorite) }.should raise_error(MakeFlaggable::Exceptions::NotFlaggedError) 103 | end 104 | 105 | it "should not raise error if flagger not flagged the flaggable with normal method" do 106 | lambda { 107 | @flagger.unflag(@flaggable, :favorite).should == false 108 | }.should_not raise_error(MakeFlaggable::Exceptions::NotFlaggedError) 109 | end 110 | end 111 | 112 | describe "toggle_flag" do 113 | it "should add a flag when none exists" do 114 | @flaggable.flaggings.length.should == 0 115 | @flagger.toggle_flag(@flaggable, :inappropriate) 116 | @flaggable.flaggings.reload.length.should == 1 117 | end 118 | 119 | it "should remove an existing flag" do 120 | @flagger.flag!(@flaggable, :inappropriate) 121 | @flaggable.flaggings.length.should == 1 122 | @flagger.toggle_flag(@flaggable, :inappropriate) 123 | @flaggable.flaggings.reload.length.should == 0 124 | end 125 | 126 | it "should return the new flagging state" do 127 | @flagger.flagged?(@flaggable, :inappropriate).should == false 128 | 129 | @flagger.toggle_flag(@flaggable, :inappropriate).should == true 130 | @flagger.flagged?(@flaggable, :inappropriate).should == true 131 | 132 | @flagger.toggle_flag(@flaggable, :inappropriate).should == false 133 | @flagger.flagged?(@flaggable, :inappropriate).should == false 134 | end 135 | end 136 | 137 | describe "flagged?" do 138 | it "should check if flagger has flagged the flaggable" do 139 | @flagger.flagged?(@flaggable, :favorite).should == false 140 | @flagger.flag!(@flaggable, :favorite) 141 | @flagger.flagged?(@flaggable, :favorite).should == true 142 | @flagger.unflag!(@flaggable, :favorite) 143 | @flagger.flagged?(@flaggable, :favorite).should == false 144 | end 145 | 146 | it "should check if flagger has flagged the flaggable with any flag" do 147 | @flagger.flagged?(@flaggable).should == false 148 | @flagger.flag!(@flaggable, :favorite) 149 | @flagger.flagged?(@flaggable).should == true 150 | @flagger.unflag!(@flaggable, :favorite) 151 | @flagger.flagged?(@flaggable).should == false 152 | end 153 | end 154 | 155 | describe "find_last_flag_for" do 156 | it "should fetch the most recent flag the flagger has made on the flaggable" do 157 | @flagger.flagged?(@flaggable, :favorite).should == false 158 | @flagger.flagged?(@flaggable, :inappropriate).should == false 159 | @flagger.flag!(@flaggable, :favorite) 160 | @flagger.flag!(@flaggable, :inappropriate) 161 | @flagger.find_last_flag_for(@flaggable).flag.should == "inappropriate" 162 | end 163 | 164 | it "should return nil when the flagger has made no flags on the flaggable" do 165 | @flagger.flagged?(@flaggable).should == false 166 | 167 | @flagger.find_last_flag_for(@flagger).should == nil 168 | end 169 | end 170 | 171 | describe "find_all_flags_for" do 172 | it "should fetch all flags the flagger has made on the flaggable" do 173 | @flagger.flagged?(@flaggable, :favorite).should == false 174 | @flagger.flagged?(@flaggable, :inappropriate).should == false 175 | @flagger.flag!(@flaggable, :favorite) 176 | @flagger.flag!(@flaggable, :inappropriate) 177 | flag_types = @flagger.find_all_flags_for(@flaggable).map(&:flag) 178 | flag_types.should include("inappropriate") 179 | flag_types.should include("favorite") 180 | end 181 | 182 | it "should return an empty ActiveRecord relation when the flagger has made no flags on the flaggable" do 183 | @flagger.flagged?(@flaggable).should == false 184 | @flagger.find_all_flags_for(@flaggable).should == [] 185 | end 186 | end 187 | end 188 | 189 | describe "flaggable" do 190 | it "should have flaggings" do 191 | @flaggable.flaggings.length.should == 0 192 | @flagger.flag!(@flaggable, :favorite) 193 | @flaggable.flaggings.reload.length.should == 1 194 | end 195 | 196 | it "should check if flaggable is flagged" do 197 | @flaggable.flagged?.should == false 198 | @flagger.flag!(@flaggable, :favorite) 199 | @flaggable.flagged?.should == true 200 | @flagger.unflag!(@flaggable, :favorite) 201 | @flaggable.flagged?.should == false 202 | end 203 | 204 | it "should remove related flaggings when destroyed" do 205 | @flagger.flag!(@flaggable, :inappropriate) 206 | @flagger.flag!(@flaggable2, :inappropriate) 207 | 208 | @flagger.flaggings.count.should == 2 209 | 210 | @flaggable.destroy 211 | @flagger.flaggings.reload.count.should == 1 212 | @flagger.flaggings.first.flaggable.should == @flaggable2 213 | 214 | @flaggable2.destroy 215 | @flagger.flaggings.reload.should be_empty 216 | end 217 | end 218 | end 219 | --------------------------------------------------------------------------------