diff --git a/.gitignore b/.gitignore index 2288186..f2d910a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,13 @@ -# rcov generated coverage - -# rdoc generated rdoc - -# yard generated doc .yardoc - -# bundler .bundle - -# jeweler generated pkg -# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: -# -# * Create a file at ~/.gitignore -# * Include files you want ignored -# * Run: git config --global core.excludesfile ~/.gitignore -# -# After doing this, these files will be ignored in all your git projects, -# saving you from having to 'pollute' every project you touch with them -# -# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) -# -# For MacOS: -# -#.DS_Store -# -# For TextMate -#*.tmproj -#tmtags -# -# For emacs: -#*~ -#\#* -#.\#* -# -# For vim: -#*.swp +.DS_Store + +*.tmproj +tmtags + +log/* \ No newline at end of file diff --git a/Gemfile b/Gemfile index 617466f..ae90bfd 100644 --- a/Gemfile +++ b/Gemfile @@ -1,11 +1,15 @@ source "http://rubygems.org" -# Add dependencies required to use your gem here. -# Example: -# gem "activesupport", ">= 2.3.5" -# Add dependencies to develop your gem here. -# Include everything needed to run rake, tests, features, etc. group :development do + gem 'mongo', '1.1.2' + gem 'bson_ext', '1.1.2' + gem 'mongo_ext' + gem 'mongo_mapper' + gem 'i18n' + + gem 'log_buddy' + + gem 'ruby-debug' gem "rspec", "~> 2.1.0" gem "yard", "~> 0.6.0" gem "bundler", "~> 1.0.0" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..ffbd3ce --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,60 @@ +GEM + remote: http://rubygems.org/ + specs: + activesupport (3.0.1) + bson (1.1.2) + bson_ext (1.1.2) + columnize (0.3.2) + diff-lcs (1.1.2) + git (1.2.5) + i18n (0.4.2) + jeweler (1.5.1) + bundler (~> 1.0.0) + git (>= 1.2.5) + rake + jnunemaker-validatable (1.8.4) + activesupport (>= 2.3.4) + linecache (0.43) + log_buddy (0.5.0) + mongo (1.1.2) + bson (>= 1.1.1) + mongo_ext (0.19.3) + mongo_mapper (0.8.6) + activesupport (>= 2.3.4) + jnunemaker-validatable (~> 1.8.4) + plucky (~> 0.3.6) + plucky (0.3.6) + mongo (~> 1.1) + rake (0.8.7) + rcov (0.9.9) + rspec (2.1.0) + rspec-core (~> 2.1.0) + rspec-expectations (~> 2.1.0) + rspec-mocks (~> 2.1.0) + rspec-core (2.1.0) + rspec-expectations (2.1.0) + diff-lcs (~> 1.1.2) + rspec-mocks (2.1.0) + ruby-debug (0.10.4) + columnize (>= 0.1) + ruby-debug-base (~> 0.10.4.0) + ruby-debug-base (0.10.4) + linecache (>= 0.3) + yard (0.6.2) + +PLATFORMS + ruby + +DEPENDENCIES + bson_ext (= 1.1.2) + bundler (~> 1.0.0) + i18n + jeweler (~> 1.5.1) + log_buddy + mongo (= 1.1.2) + mongo_ext + mongo_mapper + rcov + rspec (~> 2.1.0) + ruby-debug + yard (~> 0.6.0) diff --git a/README.rdoc b/README.rdoc index 11e72bb..f0a4d60 100644 --- a/README.rdoc +++ b/README.rdoc @@ -1,6 +1,48 @@ = mm-friendable -Description goes here. +A simple MongoMapper plugin to add friendship functionality to your application + +== Usage + +Install the gem + + gem install mm-friendable + + +Or add it to your Gemfile + + gem 'mm-friendable' + + +Then add the friendable plugin to you MongoMapper::Document + + class User + include MongoMapper::Document + plugin MongoMapper::Plugins::Friendable + + def on_add_friend(friend) + do something when a friend is added... + end + + def on_remove_friend(friend) + do something when a friend is removed... + end + end + +You must implement the on_add_friend and on_remove_friend callbacks! - however they can be empty implementations + + @user_1 = User.create + @user_2 = User.create + + @user_1.add_friend!(@user_2) + + @user_1.following_count #=> 1 + @user_1.following #=> [] + @user_1.following.first == @user_2 #=> true + + @user_2.followers_count #=> 1 + @user_2.followers #=> [] + @user_2.followers.first == @user_1 #=> true == Contributing to mm-friendable @@ -12,6 +54,7 @@ Description goes here. * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. + == Copyright Copyright (c) 2010 Luke Cunningham. See LICENSE.txt for diff --git a/lib/mm-friendable.rb b/lib/mm-friendable.rb index e69de29..90905fd 100644 --- a/lib/mm-friendable.rb +++ b/lib/mm-friendable.rb @@ -0,0 +1,3 @@ +require 'mongo_mapper' + +require File.join(File.dirname(__FILE__), 'mongo_mapper', 'plugins', 'friendable') diff --git a/lib/mongo_mapper/plugins/friendable.rb b/lib/mongo_mapper/plugins/friendable.rb new file mode 100644 index 0000000..82e7a91 --- /dev/null +++ b/lib/mongo_mapper/plugins/friendable.rb @@ -0,0 +1,76 @@ +require File.join(File.dirname(__FILE__), 'friendable', 'friend_list') + +module MongoMapper + module Plugins + module Friendable + + def self.configure(model) + model.class_eval do + key :friend_list, FriendList + + key :followers_count, Integer, :default => 0 + key :following_count, Integer, :default => 0 + + before_create :create_friend_list + + def friendable?; true; end + end + end + + module InstanceMethods + + def add_friend!(friend) + return false if friend == self + + User.push_uniq(self.id, 'friend_list.following_ids' => friend.id) + User.push_uniq(friend.id, 'friend_list.followers_ids' => self.id) + User.increment(self.id, 'following_count' => 1) + User.increment(friend.id, 'followers_count' => 1) + + on_add_friend(friend) + end + + def on_add_friend(friend) + raise NotImplementedError + end + + def remove_friend!(friend) + return false if friend == self + + User.pull(self.id, 'friend_list.following_ids' => friend.id) + User.pull(friend.id, 'friend_list.followers_ids' => self.id) + User.decrement(self.id, 'following_count' => 1) + User.decrement(friend.id, 'followers_count' => 1) + + on_remove_friend(friend) + end + + def on_remove_friend(friend) + raise NotImplementedError + end + + def followers + User.find(self.friend_list.followers_ids) + end + + def following + User.find(self.friend_list.following_ids) + end + + def following?(user) + self.friend_list.following_ids.include?(user.id) + end + + protected + def create_friend_list + self.friend_list = FriendList.new unless self.friend_list.present? + end + + end + + module ClassMethods + end + + end + end +end \ No newline at end of file diff --git a/lib/mongo_mapper/plugins/friendable/friend_list.rb b/lib/mongo_mapper/plugins/friendable/friend_list.rb new file mode 100644 index 0000000..222357f --- /dev/null +++ b/lib/mongo_mapper/plugins/friendable/friend_list.rb @@ -0,0 +1,6 @@ +class FriendList + include MongoMapper::EmbeddedDocument + + key :followers_ids, Array, :typecast => 'ObjectId' + key :following_ids, Array, :typecast => 'ObjectId' +end \ No newline at end of file diff --git a/mm-friendable.gemspec b/mm-friendable.gemspec new file mode 100644 index 0000000..c135bed --- /dev/null +++ b/mm-friendable.gemspec @@ -0,0 +1,92 @@ +# Generated by jeweler +# DO NOT EDIT THIS FILE DIRECTLY +# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{mm-friendable} + s.version = "0.0.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Luke Cunningham"] + s.date = %q{2010-11-17} + s.description = %q{A very simple mongomapper plugin to add friendship functionality to your application} + s.email = %q{luke@icaruswings.com} + s.extra_rdoc_files = [ + "LICENSE.txt", + "README.rdoc" + ] + s.files = [ + ".document", + ".rspec", + "Gemfile", + "Gemfile.lock", + "LICENSE.txt", + "README.rdoc", + "Rakefile", + "VERSION", + "lib/mm-friendable.rb", + "lib/mongo_mapper/plugins/friendable.rb", + "lib/mongo_mapper/plugins/friendable/friend_list.rb", + "spec/mm-friendable_spec.rb", + "spec/spec_helper.rb", + "spec/support/models.rb" + ] + s.homepage = %q{http://github.com/icaruswings/mm-friendable} + s.licenses = ["MIT"] + s.require_paths = ["lib"] + s.rubygems_version = %q{1.3.7} + s.summary = %q{A very simple mongomapper plugin to add friendship functionaity to your application} + s.test_files = [ + "spec/mm-friendable_spec.rb", + "spec/spec_helper.rb", + "spec/support/models.rb" + ] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 3 + + if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then + s.add_development_dependency(%q, ["= 1.1.2"]) + s.add_development_dependency(%q, ["= 1.1.2"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, [">= 0"]) + s.add_development_dependency(%q, ["~> 2.1.0"]) + s.add_development_dependency(%q, ["~> 0.6.0"]) + s.add_development_dependency(%q, ["~> 1.0.0"]) + s.add_development_dependency(%q, ["~> 1.5.1"]) + s.add_development_dependency(%q, [">= 0"]) + else + s.add_dependency(%q, ["= 1.1.2"]) + s.add_dependency(%q, ["= 1.1.2"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 2.1.0"]) + s.add_dependency(%q, ["~> 0.6.0"]) + s.add_dependency(%q, ["~> 1.0.0"]) + s.add_dependency(%q, ["~> 1.5.1"]) + s.add_dependency(%q, [">= 0"]) + end + else + s.add_dependency(%q, ["= 1.1.2"]) + s.add_dependency(%q, ["= 1.1.2"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q, ["~> 2.1.0"]) + s.add_dependency(%q, ["~> 0.6.0"]) + s.add_dependency(%q, ["~> 1.0.0"]) + s.add_dependency(%q, ["~> 1.5.1"]) + s.add_dependency(%q, [">= 0"]) + end +end + diff --git a/spec/mm-friendable_spec.rb b/spec/mm-friendable_spec.rb index 571451d..d1074c7 100644 --- a/spec/mm-friendable_spec.rb +++ b/spec/mm-friendable_spec.rb @@ -1,7 +1,89 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper') -describe "MmFriendable" do - it "fails" do - fail "hey buddy, you should probably rename this file and start specing for real" +describe "MongoMapper::Plugins::Friendable" do + + before(:each) do + @friendable = User.create(:email => 'lukec@icaruswings.com') + @friend = User.create(:email => 'luke@icaruswings.com') end + + it "should be commentable" do + @friendable.should be_friendable + end + + it "should have followers_count and following_count attributes that default to 0" do + @friendable.followers_count.should equal 0 + @friendable.following_count.should equal 0 + end + + it "should have a method for retrieving followers" do + @friendable.should respond_to(:followers) + end + + it "should have a method for retrieving friends" do + @friendable.should respond_to(:following) + end + + describe "add_vote!" do + + it "should increment the following_count attribute" do + lambda { + @friendable.add_friend!(@friend) + @friendable.reload + }.should change(@friendable, :following_count).by(1) + end + + it "should increment the followers_count attribute" do + lambda { + @friendable.add_friend!(@friend) + @friend.reload + }.should change(@friend, :followers_count).by(1) + end + + it "should add the following" do + @friendable.add_friend!(@friend) + @friendable.reload + + @friendable.following.should include(@friend) + end + + it "should add the follower" do + @friendable.add_friend!(@friend) + @friend.reload + + @friend.followers.should include(@friendable) + end + + end + end + +# describe "Comment" do +# +# before(:each) do +# @comment = Comment.new +# end +# +# it "should be embeddable?" do +# Comment.embeddable?.should be_true +# end +# +# it "should be embeddable?" do +# Comment.embeddable?.should be_true +# end +# +# it "should have a created_at key" do +# @comment.should respond_to(:created_at=) +# @comment.should respond_to(:created_at) +# end +# +# it "should have a commentor association" do +# @comment.should respond_to(:commentor_id=) +# @comment.should respond_to(:commentor_id) +# @comment.should respond_to(:commentor_type=) +# @comment.should respond_to(:commentor_type) +# +# @comment.commentor.association.should be_belongs_to +# end +# +# end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3b215e8..22a1774 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,12 +1,31 @@ +require 'rubygems' +require 'bundler/setup' + $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) + +require 'fileutils' +require 'ostruct' +require 'log_buddy' + require 'rspec' + require 'mm-friendable' # Requires supporting files with custom matchers and macros, etc, # in ./support/ and its subdirectories. Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} +log_dir = File.expand_path('../../log', __FILE__) +FileUtils.mkdir_p(log_dir) unless File.exist?(log_dir) + +logger = Logger.new(log_dir + '/test.log') +LogBuddy.init(:logger => logger) + +MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, :logger => logger) +MongoMapper.database = "mm-test-#{RUBY_VERSION.gsub('.', '-')}" +MongoMapper.database.collections.each { |c| c.drop_indexes } + + RSpec.configure do |config| - -end +end \ No newline at end of file diff --git a/spec/support/models.rb b/spec/support/models.rb new file mode 100644 index 0000000..95ecdcf --- /dev/null +++ b/spec/support/models.rb @@ -0,0 +1,11 @@ +class User + include MongoMapper::Document + plugin MongoMapper::Plugins::Friendable + + key :email, String + + def on_add_friend(friend) + true + end + +end \ No newline at end of file