Permalink
Browse files

Initial Import of Likeable Redis

  • Loading branch information...
0 parents commit 1b9062f8ed52da00520cdf3945f6bffa2e902fb3 @schneems schneems committed Oct 11, 2011
1 .autotest
@@ -0,0 +1 @@
+require 'autotest/growl'
3 .gitignore
@@ -0,0 +1,3 @@
+Gemfile.lock
+/pkg
+/vendor
2 .rspec
@@ -0,0 +1,2 @@
+--format nested
+--color
19 .rvmrc
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+ruby_string="ree"
+gemset_name="likeable"
+
+if rvm list strings | grep -q "${ruby_string}" ; then
+
+ rvm use "${ruby_string}@${gemset_name}" --create
+
+ # Complain if bundler isn't installed
+ if [[ -z "`gem which bundler 2>&1 | grep -v ERROR`" ]]; then
+ echo "You need bundler:"
+ echo ""
+ echo " gem install bundler"
+ echo ""
+ fi
+else
+ echo "${ruby_string} was not found, please run 'rvm install ${ruby_string}' and then cd back into the project directory."
+fi
18 Gemfile
@@ -0,0 +1,18 @@
+source 'http://rubygems.org'
+ gem 'activesupport'
+ gem 'keytar', '>=1.5.2'
+ gem 'redis'
+
+
+group :development, :test do
+ gem 'activerecord', '~>3.0.4' ## not needed if you're just using KeyBuilder
+ gem 'rake', '~>0.8.7'
+ gem 'jeweler', '~>1.5.2'
+ gem "autotest-standalone"
+ gem "autotest-growl"
+end
+
+group :test do
+ gem 'sqlite3', '~> 1.3.3'
+ gem 'rspec', '~> 2.5'
+end
85 README.md
@@ -0,0 +1,85 @@
+Use Redis to Make your Ruby objects Likeable!
+======
+
+You like this
+-------------
+Likeable is the easiest way to allow your models to be liked by users, just drop a few lines of code into your model and you're good to go.
+
+```ruby
+
+ class Comment
+ attr_accessor :id
+
+ # ...
+ end
+
+ Likeable.setup do |likeable|
+ likeable.classes = [Comment]
+ likeable.redis = Redis.new
+ end
+
+ comment = Comment.find(15)
+ comment.like_count # => 0
+ current_user.like!(comment) # => #<Likeable::Like ... >
+ comment.like_count # => 1
+ comment.likes # => [#<Likeable::Like ... >]
+ comment.likes.last.user # => #<User ... >
+ comment.likes.last.created_at # => Wed Jul 27 19:34:32 -0500 2011
+
+ comment.liked_by?(current_user) # => true
+
+ current_user.all_liked(Comment) # => [#<Comment ...>, ...]
+```
+
+
+Setup
+=======
+Gemfile:
+
+ gem 'Likeable'
+
+Next set up your Redis connection and specify models to like in initializers/likeable.rb:
+
+```ruby
+ Likeable.setup do |likeable|
+ likeable.classes = [Comment, Spot]
+ likeable.redis = Redis.new
+ end
+```
+
+
+Thats about it.
+
+ RedisRed RedisRedi
+ RedisRedisRedi RedisRedisRedisR
+ RedisRedisRedisRedi RedisRedisRedisRedi
+ RedisRedisRedisRedisRedisRedisRe Redi
+ RedisRedisRedisRedisRedisRedisRe Redi
+ RedisRedisRedisRedisRedisRedisRedisR Redi
+ RedisRedisRedisRedisRedisRedisRedisRedis R
+ RedisRedisRedisRedisRedisRedisRedisRedisRedi Red
+ RedisRedisRedisRedisRedisRedisRedisRedisRedisRe R
+ RedisRedisRedisRedisRedisRedisRedisRedisRedisRedi
+ RedisRedisRedisRedisRedisRedisRedisRedisRedisRedi
+ RedisRedisRedisRedisRedisRedisRedisRedisRedisRe
+ RedisRedisRedisRedisRedisRedisRedisRedisRedis
+ RedisRedisRedisRedisRedisRedisRedisRedisRe
+ RedisRedisRedisRedisRedisRedisRedisRe
+ RedisRedisRedisRedisRedisRedisR
+ RedisRedisRedisRedisRedis
+ RedisRedisRedisRedis
+ RedisRedisRed
+ RedisRedi
+ RedisR
+ Redi
+ Re
+
+Contribution
+============
+
+Fork away. If you want to chat about a feature idea, or a question you can find me on the twitters [@schneems](http://twitter.com/schneems). Put any major changes into feature branches. Make sure all tests stay green, and make sure your changes are covered.
+
+
+licensed under MIT License
+Copyright (c) 2011 Schneems. See LICENSE.txt for
+further details.
26 Rakefile
@@ -0,0 +1,26 @@
+require 'rubygems'
+require 'bundler'
+begin
+ Bundler.setup(:default, :development)
+rescue Bundler::BundlerError => e
+ $stderr.puts e.message
+ $stderr.puts "Run `bundle install` to install missing gems"
+ exit e.status_code
+end
+require 'rake'
+
+require 'jeweler'
+Jeweler::Tasks.new do |gem|
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
+ gem.name = "likeable"
+ gem.homepage = "http://github.com/Schnems/likeable"
+ gem.license = "MIT"
+ gem.summary = %Q{Like ruby objects backed by redis}
+ gem.description = %Q{
+ Likeable allows you to make your models...well...likeable using redis.
+ }
+ gem.email = "richard.schneeman@gmail.com"
+ gem.authors = ["Schneems"]
+ gem.add_development_dependency "rspec"
+end
+Jeweler::RubygemsDotOrgTasks.new
1 autotest/discover.rb
@@ -0,0 +1 @@
+Autotest.add_discovery { "rspec2" }
BIN lib/.DS_Store
Binary file not shown.
115 lib/likeable.rb
@@ -0,0 +1,115 @@
+require 'active_support/concern'
+require 'keytar'
+
+
+module Likeable
+ extend ActiveSupport::Concern
+
+ included do
+ include Keytar
+ include Likeable::Facepile
+ define_key :like, :key_case => nil
+
+ if self.respond_to?(:after_destroy)
+ after_destroy :destroy_all_likes
+ else
+ warn "#{self} doesn't support after_destroy callback, likes will not be cleared automatically when object is destroyed"
+ end
+ end
+
+ def destroy_all_likes
+ liked_users.each {|user| self.remove_like_from(user) }
+ end
+
+ # create a like
+ # the user who created the like has a reference to the object liked
+ def add_like_from(user, time = Time.now.to_f)
+ Likeable.redis.hset(like_key, user.id, time)
+ Likeable.redis.hset(user.like_key(self.class.to_s.downcase), self.id, time)
+ like = Like.new(:target => self, :user => user, :time => time)
+ after_like(like)
+ clear_memoized_methods(:like_count, :like_user_ids, :liked_user_ids, :liked_users, :likes)
+ like
+ end
+
+ def clear_memoized_methods(*methods)
+ methods.each do |method|
+ eval("@#{method} = nil")
+ end
+ end
+
+ def after_like(like)
+ Likeable.after_like.call(like)
+ end
+
+ # removes a like
+ def remove_like_from(user)
+ Likeable.redis.hdel(like_key, user.id)
+ Likeable.redis.hdel(user.like_key(self.class.to_s.downcase), self.id)
+ clear_memoized_methods(:like_count, :like_user_ids, :liked_user_ids, :liked_users)
+ end
+
+ def like_count
+ @like_count ||= @like_user_ids.try(:count) || @likes.try(:count) || Likeable.redis.hlen(like_key)
+ end
+
+ # get all user ids that have liked a target object
+ def like_user_ids
+ @like_user_ids ||= (Likeable.redis.hkeys(like_key)||[]).map(&:to_i)
+ end
+
+ def liked_users(limit = nil)
+ @liked_users ||= User.where(:id => like_user_ids)
+ end
+
+ def likes
+ @likes ||= begin
+ Likeable.redis.hgetall(like_key).collect do |user_id, time|
+ Like.new(:user_id => user_id, :time => time, :target => self)
+ end
+ end
+ end
+
+ # did given user like the object
+ def liked_by?(user)
+ return false unless user
+ liked_by = @like_user_ids.include?(user.id) if @like_user_ids
+ liked_by ||= Likeable.redis.hexists(like_key, user.id)
+ end
+
+
+ def likeable_resource_name
+ Likeable.get_resource_name_for_class(self.class)
+ end
+
+
+ ### Class Methods ###
+ # ----------------- #
+ # allows us to setup callbacks when creating likes
+ # after_like :notify_users
+ module ClassMethods
+
+ def all_liked_ids_by(user)
+ key = user.like_key(self.to_s.downcase)
+ ids = (Likeable.redis.hkeys(key)||[]).map(&:to_i)
+ end
+
+ def all_liked_by(user)
+ ids = all_liked_ids_by(user)
+ self.where(:id => ids)
+ end
+
+ def after_like(*methods)
+ define_method(:after_like) do |like|
+ methods.each do |method|
+ eval("#{method}(like)")
+ end
+ end
+ end
+ end
+end
+
+require 'likeable/like'
+require 'likeable/facepile'
+require 'likeable/user_methods'
+require 'likeable/module_methods'
27 lib/likeable/facepile.rb
@@ -0,0 +1,27 @@
+module Likeable
+ module Facepile
+ # returns friend of user who like target
+ def liked_friends(user, limit = nil)
+ friend_ids = liked_friend_ids(user)
+ friend_ids = friend_ids.sample(limit) unless limit.blank?
+ @liked_friends ||= Likeable.find_many(User, friend_ids)
+ end
+
+ def liked_friend_ids(user)
+ @liked_friend_ids ||= like_user_ids & user.friend_ids
+ end
+
+ def ids_for_facepile(user, limit = Likeable.facepile_default_limit)
+ ids = liked_friend_ids(user).shuffle + like_user_ids.shuffle # show friends first
+ ids.uniq.first(limit)
+ end
+
+ def users_for_facepile(user, limit = Likeable.facepile_default_limit)
+ return [] if user.blank?
+ @facepile ||= begin
+ return nil unless ids = ids_for_facepile(user, limit)
+ Likeable.find_many(User, ids)
+ end
+ end
+ end
+end
31 lib/likeable/like.rb
@@ -0,0 +1,31 @@
+require 'digest/sha1'
+
+class Likeable::Like
+ attr_accessor :created_at, :target, :like_user, :user_id
+
+ def initialize(options = {})
+ self.created_at = Time.at(options[:time].try(:to_f)||Time.now)
+ self.target = options[:target]
+ self.user_id = options[:user].try(:id) || options[:user_id]
+ self.like_user = options[:user]
+ end
+
+ def id
+ Digest::SHA1.hexdigest("#{user_id}#{target.class}#{target.id}#{created_at}")
+ end
+
+ def user
+ @user ||= like_user
+ @user ||= Likeable.find_one(User, user_id)
+ @user
+ end
+
+ def to_hash(type=:full)
+ {
+ :created_at => created_at.iso8601,
+ :type => target.class.name.gsub(/^[A-Za-z]+::/, '').underscore.downcase.to_sym,
+ :target => target.to_hash(type),
+ :user => user.to_hash(type)
+ }
+ end
+end
111 lib/likeable/module_methods.rb
@@ -0,0 +1,111 @@
+require 'redis'
+
+module Likeable
+ mattr_accessor :facepile_default_limit
+ self.facepile_default_limit = 9
+
+
+ ### Module Methods ###
+ # ------------------ #
+ class << self
+ def classes
+ @classes||[]
+ end
+
+ def classes=(*args)
+ @classes = args
+ end
+
+ # Likeable.model("Highlight")
+ # ------------------------- #
+ # turns a string into a model
+ # "Highlight".constantize # => Highlight; "Hi1i6ht".constantize = #=> false
+ def model(target_model)
+ target_model.camelcase.constantize
+ rescue NameError => ex
+ return false
+ end
+
+ # Likeable.find_by_resource_id("highlight", 22)
+ # ---------------------------------------- #
+ # #<Highlight id: ... # if highlight 22 exists
+ # nil # if highlight 22 does not exist
+ def find_by_resource_id(resource_name, target_id)
+ target = Likeable.get_class_for_resource_name(resource_name)
+ if target.present?
+ Likeable.find_one(target, target_id)
+ else
+ false
+ end
+ end
+
+ # Likeable.get_class_for_resource_name('photo')
+ # ------------------------- #
+ # Returns the class for the resource name
+ def get_class_for_resource_name(resource_name)
+ self.model(resource_name)
+ end
+
+ def redis
+ @redis ||= Redis.new
+ end
+
+ def redis=(redis)
+ @redis = redis
+ end
+
+ def after_like(&block)
+ @after_like = block if block.present?
+ @after_like ||= lambda {}
+ @after_like
+ end
+
+ def find_many(klass, ids)
+ @find_many ||= lambda {|klass, ids| klass.where(:id => ids)}
+ @find_many.call(klass, ids)
+ end
+
+
+ def find_one(klass, id)
+ @find_one ||= lambda {|klass, ids| klass.where(:id => id)}
+ @find_one.call(klass, id)
+ end
+
+ def find_one=(find_one)
+ @find_one = find_one
+ end
+
+ def user_class
+ @user_class ||= ::User
+ end
+
+ def user_class=(user)
+ raise "User must be a class #{user.inspect}" unless user.class == Class
+ @user_class = user
+ end
+
+ # Likeable.setup do |like|
+ # like.redis = Redis.new(#...)
+ # like.find_one = lambda {|klass, id | klass.where(:id => id)}
+ # like.find_many = lambda {|klass, ids| klass.where(:id => ids)}
+ # like.classes = Spot, Comment
+ # end
+
+ def make_classes_likeable
+ classes.each do |klass|
+ klass.send :include, Likeable
+ end
+ end
+
+ def give_users_like_ability
+ user_class.send :include, ::Likeable::UserMethods
+ end
+
+ def setup(&block)
+ yield self unless block.blank?
+ make_classes_likeable
+ give_users_like_ability
+ true
+ end
+ end
+end
36 lib/likeable/user_methods.rb
@@ -0,0 +1,36 @@
+module Likeable::UserMethods
+ extend ActiveSupport::Concern
+
+ included do
+ include Keytar
+ define_key :like, :key_case => nil
+ end
+
+
+ def like!(obj)
+ obj.add_like_from(self)
+ end
+
+ def unlike!(obj)
+ obj.remove_like_from(self)
+ end
+
+ def like?(obj)
+ obj.liked_by?(self)
+ end
+ alias :likes? :like?
+
+ def friend_ids_that_like(obj)
+ obj.liked_friend_ids(self)
+ end
+
+ def friends_that_like(obj, limit = nil)
+ obj.liked_friends(self, limit)
+ end
+
+ # @user.liked(Spot)
+ # will return all spots that user has liked
+ def all_liked(klass)
+ klass.all_liked_by(self)
+ end
+end
22 license.txt
@@ -0,0 +1,22 @@
+licensed under MIT License:
+
+Copyright (c) 2011 Schneems
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
56 spec/likeable/facepile_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+class CleanTestClassForLikeable
+ include Likeable
+ def like_key
+ "like_key"
+ end
+
+ def to_hash(*args); {} end
+
+ def foo
+ end
+
+ def id
+ @id ||= rand(100)
+ end
+end
+
+
+describe Likeable::Facepile do
+ before(:each) do
+ @user = User.new
+ @target = CleanTestClassForLikeable.new
+ end
+
+ describe 'facepile' do
+ before do
+ @friend_ids = [1,2,3,4]
+ @like_ids = [3,4,5,6,7,8,9,10,11,12]
+ @intersection = @friend_ids & @like_ids
+ end
+ describe '#ids_for_facepile' do
+ it 'builds a array of ids with friend ids and randoms if they have liked the object up to the limit' do
+ @target.should_receive(:liked_friend_ids).with(@user).and_return(@friend_ids)
+ @target.should_receive(:like_user_ids).and_return(@like_ids)
+ @target.ids_for_facepile(@user).should include(@intersection.sample)
+ end
+
+ it 'can be limited' do
+ limit = 3
+ @target.should_receive(:liked_friend_ids).and_return(@friend_ids)
+ @target.should_receive(:like_user_ids).and_return(@like_ids)
+ @target.ids_for_facepile(@user, limit).count.should eq(limit)
+ end
+ end
+
+ describe '#users_for_facepile' do
+ it 'builds a array of users if they have liked the object' do
+ @target.should_receive(:ids_for_facepile).and_return(@friend_ids)
+ User.should_receive(:where).with(:id => @friend_ids)
+ @target.users_for_facepile(@user)
+ end
+ end
+
+ end
+end
19 spec/likeable/like_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+describe Likeable::Like do
+ before do
+ @time = Time.now
+ end
+ describe 'attributes' do
+ it 'stores target, user, and created_at' do
+ like = Likeable::Like.new(:target => @target, :user => @user, :time => @time)
+ like.user.should eq(@user)
+ like.target.should eq(@target)
+ like.created_at.should eq(@time)
+ end
+
+ it 'converts float time to propper Time object' do
+ like = Likeable::Like.new(:time => @time.to_f)
+ like.created_at.should eq(@time)
+ end
+ end
+end
63 spec/likeable/module_methods_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+
+class CleanTestClassForLikeable
+ include Likeable
+ def like_key
+ "like_key"
+ end
+
+ def to_hash(*args); {} end
+
+ def foo
+ end
+
+ def id
+ @id ||= rand(100)
+ end
+end
+
+class LikeableIncludedInSetup
+ def like_key
+ "like_key"
+ end
+
+ def id
+ @id ||= rand(100)
+ end
+end
+
+describe Likeable::Facepile do
+ before(:each) do
+ @user = User.new
+ @target = CleanTestClassForLikeable.new
+ end
+
+ describe "module methods" do
+ describe ".model" do
+ it "takes a valid class string and turns it into a class" do
+ klass = CleanTestClassForLikeable
+ klass_name = klass.to_s
+ Likeable.model(klass_name).should eq(klass)
+ end
+ end
+
+ describe ".find_by_resource_id" do
+ it "finds an active-record based object on a valid model and id" do
+ klass = CleanTestClassForLikeable
+ klass_name = klass.to_s
+ id = rand(1000)
+ klass.should_receive(:where).with(:id => id).and_return([])
+ Likeable.find_by_resource_id(klass_name, id)
+ end
+
+ it "will return nil for an invalid object" do
+ klass = CleanTestClassForLikeable
+ klass_name = klass.to_s + "this makes this klass_name invalid"
+
+ Likeable.find_by_resource_id(klass_name, rand(1000)).should be_blank
+ end
+ end
+
+ end
+
+end
66 spec/likeable/setup_spec.rb
@@ -0,0 +1,66 @@
+require 'spec_helper'
+
+
+class LikeableIncludedInSetup
+
+end
+
+class SetupCleanTestClassForLikeable
+ include Likeable
+ def like_key
+ "like_key"
+ end
+
+ def to_hash(*args); {} end
+
+ def foo
+ end
+
+ def id
+ @id ||= rand(100)
+ end
+end
+
+describe Likeable do
+ before(:each) do
+ reload_user!
+ Likeable.user_class = User
+ @user = User.new
+ @target = SetupCleanTestClassForLikeable.new
+ end
+
+
+ describe "Likeable setup" do
+ it "adds Likeable::UserMethods to User" do
+ describe User.ancestors do
+ it {should_not include Likeable::UserMethods}
+ end
+ Likeable.setup
+ describe User.ancestors do
+ it {should include Likeable::UserMethods}
+ end
+ end
+
+ it "adds likeable to class" do
+ Likeable.setup do |like|
+ like.classes = LikeableIncludedInSetup
+ end
+
+ describe LikeableIncludedInSetup.ancestors do
+ it {should include Likeable}
+ end
+ end
+
+ it "" do
+ result = "foo"
+ Likeable.setup
+
+ Likeable.after_like do |like|
+ result = "after_like_called_successfully"
+ end
+
+ @user.like! @target
+ result.should == "after_like_called_successfully"
+ end
+ end
+end
68 spec/likeable/user_methods_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+class CleanTestClassForLikeable
+ include Likeable
+ def like_key
+ "like_key"
+ end
+
+ def to_hash(*args); {} end
+
+ def foo
+ end
+
+ def id
+ @id ||= rand(100)
+ end
+end
+
+Likeable.setup
+
+describe Likeable::UserMethods do
+ before(:each) do
+ @user = User.new
+ @target = CleanTestClassForLikeable.new
+ end
+
+ describe '#like!' do
+ it "calls add_like_from in target" do
+ @target.should_receive(:add_like_from).with(@user)
+ @user.like! @target
+ end
+ end
+
+ describe '#unlike!' do
+ it "calls remove_like_from in target" do
+ @target.should_receive(:remove_like_from).with(@user)
+ @user.unlike! @target
+ end
+ end
+
+ describe '#like?' do
+ it "calls liked_by? in target" do
+ @target.should_receive(:liked_by?).with(@user)
+ @user.like? @target
+ end
+ end
+
+ describe '#like?' do
+ it "calls liked_by? in target" do
+ @target.should_receive(:liked_by?).with(@user)
+ @user.like? @target
+ end
+ end
+
+ describe '#friend_ids_that_like' do
+ it "calls liked_friend_ids? in target" do
+ @target.should_receive(:liked_friend_ids).with(@user)
+ @user.friend_ids_that_like @target
+ end
+ end
+
+ describe '#friends_that_like' do
+ it "calls liked_friends? in target" do
+ @target.should_receive(:liked_friends).with(@user, nil)
+ @user.friends_that_like @target
+ end
+ end
+end
103 spec/likeable_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+class CleanTestClassForLikeable
+ include Likeable
+ def like_key
+ "like_key"
+ end
+
+ def to_hash(*args); {} end
+
+ def foo
+ end
+
+ def id
+ @id ||= rand(100)
+ end
+end
+
+
+
+
+Likeable.setup do |like|
+ like.find_one = lambda {|klass, id| klass.where(:id => id)}
+end
+
+
+describe Likeable do
+
+ before(:each) do
+ @user = User.new
+ @target = CleanTestClassForLikeable.new
+ end
+ describe 'instance methods' do
+
+ describe "#add_like_from" do
+ it "creates a like" do
+ target_class = @target.class.to_s.downcase
+ user_like_key = "users:like:#{@user.id}:#{target_class}"
+ time = Time.now.to_f
+ @user.should_receive(:like_key).with(target_class).and_return(user_like_key)
+ Likeable.redis.should_receive(:hset).with("like_key", @user.id, time).once
+ Likeable.redis.should_receive(:hset).with(user_like_key, @target.id, time).once
+ @target.add_like_from(@user, time)
+ end
+ end
+
+ describe "#remove_like_from" do
+ it "removes a like" do
+ target_class = @target.class.to_s.downcase
+ user_like_key = "users:like:#{@user.id}:#{target_class}"
+ @user.should_receive(:like_key).with(target_class).and_return(user_like_key)
+ Likeable.redis.should_receive(:hdel).with("like_key", @user.id).once
+ Likeable.redis.should_receive(:hdel).with(user_like_key, @target.id)
+ @target.remove_like_from(@user)
+ end
+ end
+
+ describe "#likes" do
+ it "returns set of likes" do
+ Likeable.redis.should_receive(:hkeys).with("like_key").once
+ @target.like_user_ids
+ end
+ end
+
+ describe "#liked_by?" do
+ it "will answer if current user likes target" do
+ Likeable.redis.should_receive(:hexists).with("like_key", @user.id).once
+ @target.liked_by?(@user)
+ end
+ end
+
+ describe "#liked_friend_ids" do
+ it "will return all friend ids of user who like target" do
+ common_value = 3
+ @target.should_receive(:like_user_ids).and_return([1,2, common_value])
+ @user.should_receive(:friend_ids).and_return([common_value])
+ @target.liked_friend_ids(@user).should == [common_value]
+ end
+ end
+
+ describe "#liked_friends" do
+ it "will return all friends who like object" do
+ values = [1]
+ @target.should_receive(:liked_friend_ids).with(@user).and_return(values)
+ User.should_receive(:where).with(:id => values)
+ @target.liked_friends(@user)
+ end
+ end
+ end
+
+ describe "class methods" do
+ describe 'after_like' do
+ it 'should be a class method when included' do
+ CleanTestClassForLikeable.respond_to?(:after_like).should be_true
+ end
+ it 'is called after a like is created' do
+ CleanTestClassForLikeable.after_like(:foo)
+ @target.should_receive(:foo)
+ @target.add_like_from(@user)
+ end
+ end
+ end
+
+end
44 spec/spec_helper.rb
@@ -0,0 +1,44 @@
+require 'rubygems'
+require 'active_record'
+
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../..', 'lib'))
+
+
+def build_user!
+ eval %Q{
+ class User
+ def id
+ @time ||= Time.now.to_f.to_s
+ end
+
+ def self.where(*args)
+ end
+
+ def friends_ids
+ []
+ end
+
+ def self.after_destroy
+ end
+ end
+ }
+end
+
+def unload_user!
+ Object.instance_eval{ remove_const :User }
+end
+
+def reload_user!
+ unload_user!
+ build_user!
+end
+
+build_user!
+
+require 'likeable'
+
+require 'rspec'
+require 'rspec/autorun'
+

0 comments on commit 1b9062f

Please sign in to comment.