recommendable 0.1.2 → 0.1.3
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/CHANGELOG.markdown +16 -0
- data/README.markdown +9 -3
- data/TODO +2 -3
- data/app/{jobs → workers}/recommendable/recommendation_refresher.rb +3 -2
- data/lib/recommendable/acts_as_recommendable.rb +14 -1
- data/lib/recommendable/acts_as_recommended_to.rb +34 -33
- data/lib/recommendable/railtie.rb +9 -0
- data/lib/recommendable/version.rb +1 -1
- data/recommendable.gemspec +1 -1
- data/spec/dummy/recommendable_dummy_test +0 -0
- data/spec/models/user_benchmark_spec.rb +2 -2
- metadata +26 -25
    
        data/CHANGELOG.markdown
    ADDED
    
    | @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            Changelog
         | 
| 2 | 
            +
            =========
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            0.1.2 (current version)
         | 
| 5 | 
            +
            -----------------------
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            * Fix an issue that could cause similarity values between users to be incorrect.
         | 
| 8 | 
            +
            * `User#common_likes_with` and `User#common_dislikes_with` now return the actual Model instances by default. Accordingly, these methods are no longer private.
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            0.1.1
         | 
| 11 | 
            +
            -----
         | 
| 12 | 
            +
            * Introduce the "stashed item" feature. This gives the ability for users to save an item in a list to try later, while at the same time removing it from their list of recommendations. This lets your users keep getting new recommendations without necessarily having to rate what they know they want to try.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            0.1.0
         | 
| 15 | 
            +
            -----
         | 
| 16 | 
            +
            * Welcome to recommendable!
         | 
    
        data/README.markdown
    CHANGED
    
    | @@ -29,7 +29,9 @@ Add the following to your Rails application's `Gemfile`: | |
| 29 29 |  | 
| 30 30 | 
             
            After your `bundle install`, you can then run:
         | 
| 31 31 |  | 
| 32 | 
            -
             | 
| 32 | 
            +
            ``` bash
         | 
| 33 | 
            +
            $ rails g recommendable:install (--user-class=User)
         | 
| 34 | 
            +
            ```
         | 
| 33 35 |  | 
| 34 36 | 
             
            After running the installation generator, you should double check
         | 
| 35 37 | 
             
            `config/initializers/recommendable.rb` for options on configuring your Redis
         | 
| @@ -42,7 +44,9 @@ to wait. In fact, you can run multiple resque workers if you wish. | |
| 42 44 |  | 
| 43 45 | 
             
            Assuming you have `redis-server` running...
         | 
| 44 46 |  | 
| 45 | 
            -
             | 
| 47 | 
            +
            ``` bash
         | 
| 48 | 
            +
            $ QUEUE=recommendable rake environment resque:work
         | 
| 49 | 
            +
            ```
         | 
| 46 50 |  | 
| 47 51 | 
             
            You can run this command multiple times if you wish to start more than one
         | 
| 48 52 | 
             
            worker. This is the standard rake task for starting a Resque worker so, for
         | 
| @@ -194,6 +198,8 @@ Movie.find_by_title('Star Wars: Episode I - The Phantom Menace').disliked_by | |
| 194 198 | 
             
            #=> [#<User username: 'davidcelis'>]
         | 
| 195 199 | 
             
            current_user.common_likes_with(friend)
         | 
| 196 200 | 
             
            #=> [#<Movie title: '2001: A Space Odyssey', year: 1968>]
         | 
| 201 | 
            +
            current_user.common_likes_with(friend, :class => Show)
         | 
| 202 | 
            +
            #=> []
         | 
| 197 203 | 
             
            ```
         | 
| 198 204 |  | 
| 199 205 | 
             
            `common_dislikes_with` and `disagreements_with` are available for similar use.
         | 
| @@ -322,7 +328,7 @@ Links | |
| 322 328 | 
             
            -----
         | 
| 323 329 | 
             
            * Code: `git clone git://github.com/davidcelis/recommendable.git`
         | 
| 324 330 | 
             
            * Home: <http://github.com/davidcelis/recommendable>
         | 
| 325 | 
            -
            * Docs: <http://rubydoc.info/gems/recommendable/ | 
| 331 | 
            +
            * Docs: <http://rubydoc.info/gems/recommendable/frames>
         | 
| 326 332 | 
             
            * Bugs: <http://github.com/davidcelis/recommendable/issues>
         | 
| 327 333 | 
             
            * Gems: <http://rubygems.org/gems/recommendable>
         | 
| 328 334 |  | 
    
        data/TODO
    CHANGED
    
    | @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            = TODO
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
            * Make public methods to return likes/dislikes that are common between two users
         | 
| 3 | 
            +
            * Recalculate similarity values ONLY for users that have liked/disliked the item
         | 
| 4 | 
            +
              that self just liked/disliked
         | 
| 6 5 | 
             
            * Allow the option NOT to queue up on like/dislike/ignore?
         | 
| @@ -3,9 +3,10 @@ module Recommendable | |
| 3 3 | 
             
                include Resque::Plugins::UniqueJob
         | 
| 4 4 | 
             
                @queue = :recommendable
         | 
| 5 5 |  | 
| 6 | 
            -
                def self.perform(user_id)
         | 
| 6 | 
            +
                def self.perform(user_id, other_ids)
         | 
| 7 7 | 
             
                  user = Recommendable.user_class.find(user_id)
         | 
| 8 | 
            -
                   | 
| 8 | 
            +
                  return if other_ids.empty?
         | 
| 9 | 
            +
                  user.send :update_similarities, other_ids
         | 
| 9 10 | 
             
                  user.send :update_recommendations
         | 
| 10 11 | 
             
                end
         | 
| 11 12 | 
             
              end
         | 
| @@ -22,6 +22,19 @@ module Recommendable | |
| 22 22 | 
             
                      def has_been_rated?
         | 
| 23 23 | 
             
                        likes.count + dislikes.count > 0
         | 
| 24 24 | 
             
                      end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      # Returns an array of users that have liked or disliked this item.
         | 
| 27 | 
            +
                      # @return [Array] an array of users
         | 
| 28 | 
            +
                      def rated_by
         | 
| 29 | 
            +
                        liked_by + disliked_by
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
                      
         | 
| 32 | 
            +
                      # Returns an array of IDs of users that have liked or disliked this item.
         | 
| 33 | 
            +
                      # @return [Array] an array of user IDs
         | 
| 34 | 
            +
                      # @private
         | 
| 35 | 
            +
                      def rates_by
         | 
| 36 | 
            +
                        likes.map(&:user_id) + dislikes.map(&:user_id)
         | 
| 37 | 
            +
                      end
         | 
| 25 38 |  | 
| 26 39 | 
             
                      # Used for setup purposes. Calls convenience methods to create sets
         | 
| 27 40 | 
             
                      # in redis of users that both like and dislike this object.
         | 
| @@ -39,7 +52,7 @@ module Recommendable | |
| 39 52 | 
             
                        Recommendable.redis.del "#{self.class}:#{id}:disliked_by"
         | 
| 40 53 | 
             
                      end
         | 
| 41 54 |  | 
| 42 | 
            -
                      private :likes, :dislikes, :ignores, :stashes,
         | 
| 55 | 
            +
                      private :likes, :dislikes, :ignores, :stashes, :rates_by,
         | 
| 43 56 | 
             
                              :create_recommendable_sets, :destroy_recommendable_sets
         | 
| 44 57 | 
             
                    end
         | 
| 45 58 | 
             
                  end
         | 
| @@ -42,12 +42,9 @@ module Recommendable | |
| 42 42 | 
             
                  def like(object)
         | 
| 43 43 | 
             
                    raise RecordNotRecommendableError unless object.recommendable?
         | 
| 44 44 | 
             
                    return if likes?(object)
         | 
| 45 | 
            -
                     | 
| 46 | 
            -
                    unstash(object)
         | 
| 47 | 
            -
                    unignore(object)
         | 
| 48 | 
            -
                    unpredict(object)
         | 
| 45 | 
            +
                    completely_unrecommend(object)
         | 
| 49 46 | 
             
                    likes.create!(:likeable_id => object.id, :likeable_type => object.class.to_s)
         | 
| 50 | 
            -
                    Resque.enqueue RecommendationRefresher, self.id
         | 
| 47 | 
            +
                    Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
         | 
| 51 48 | 
             
                    true
         | 
| 52 49 | 
             
                  end
         | 
| 53 50 |  | 
| @@ -65,7 +62,7 @@ module Recommendable | |
| 65 62 | 
             
                  # @return true if object is unliked, nil if nothing happened
         | 
| 66 63 | 
             
                  def unlike(object)
         | 
| 67 64 | 
             
                    if likes.where(:likeable_id => object.id, :likeable_type => object.class.to_s).first.try(:destroy)
         | 
| 68 | 
            -
                      Resque.enqueue RecommendationRefresher, self.id
         | 
| 65 | 
            +
                      Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
         | 
| 69 66 | 
             
                      true
         | 
| 70 67 | 
             
                    end
         | 
| 71 68 | 
             
                  end
         | 
| @@ -114,12 +111,9 @@ module Recommendable | |
| 114 111 | 
             
                  def dislike(object)
         | 
| 115 112 | 
             
                    raise RecordNotRecommendableError unless object.recommendable?
         | 
| 116 113 | 
             
                    return if dislikes?(object)
         | 
| 117 | 
            -
                     | 
| 118 | 
            -
                    unstash(object)
         | 
| 119 | 
            -
                    unignore(object)
         | 
| 120 | 
            -
                    unpredict(object)
         | 
| 114 | 
            +
                    completely_unrecommend(object)
         | 
| 121 115 | 
             
                    dislikes.create!(:dislikeable_id => object.id, :dislikeable_type => object.class.to_s)
         | 
| 122 | 
            -
                    Resque.enqueue RecommendationRefresher, self.id
         | 
| 116 | 
            +
                    Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
         | 
| 123 117 | 
             
                    true
         | 
| 124 118 | 
             
                  end
         | 
| 125 119 |  | 
| @@ -137,7 +131,7 @@ module Recommendable | |
| 137 131 | 
             
                  # @return true if object is removed from self's dislikes, nil if nothing happened
         | 
| 138 132 | 
             
                  def undislike(object)
         | 
| 139 133 | 
             
                    if dislikes.where(:dislikeable_id => object.id, :dislikeable_type => object.class.to_s).first.try(:destroy)
         | 
| 140 | 
            -
                      Resque.enqueue RecommendationRefresher, self.id
         | 
| 134 | 
            +
                      Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
         | 
| 141 135 | 
             
                      true
         | 
| 142 136 | 
             
                    end
         | 
| 143 137 | 
             
                  end
         | 
| @@ -252,10 +246,7 @@ module Recommendable | |
| 252 246 | 
             
                  def ignore(object)
         | 
| 253 247 | 
             
                    raise RecordNotRecommendableError unless object.recommendable?
         | 
| 254 248 | 
             
                    return if has_ignored?(object)
         | 
| 255 | 
            -
                     | 
| 256 | 
            -
                    undislike(object)
         | 
| 257 | 
            -
                    unstash(object)
         | 
| 258 | 
            -
                    unpredict(object)
         | 
| 249 | 
            +
                    completely_unrecommend(object)
         | 
| 259 250 | 
             
                    ignores.create!(:ignoreable_id => object.id, :ignoreable_type => object.class.to_s)
         | 
| 260 251 | 
             
                    true
         | 
| 261 252 | 
             
                  end
         | 
| @@ -309,10 +300,6 @@ module Recommendable | |
| 309 300 | 
             
                end
         | 
| 310 301 |  | 
| 311 302 | 
             
                module RecommendationMethods
         | 
| 312 | 
            -
                  def self.acts_as_recommended_to? ; true ; end
         | 
| 313 | 
            -
             | 
| 314 | 
            -
                  def can_receive_recommendations? ; self.class.acts_as_recommended_to? ; end
         | 
| 315 | 
            -
             | 
| 316 303 | 
             
                  # Checks to see if self has already liked or disliked a passed object.
         | 
| 317 304 | 
             
                  # 
         | 
| 318 305 | 
             
                  # @param [Object] object the object you want to check
         | 
| @@ -362,15 +349,13 @@ module Recommendable | |
| 362 349 |  | 
| 363 350 | 
             
                    unioned_predictions = "#{self.class}:#{id}:predictions"
         | 
| 364 351 | 
             
                    Recommendable.redis.zunionstore unioned_predictions, Recommendable.recommendable_classes.map {|klass| predictions_set_for(klass)}
         | 
| 365 | 
            -
                    return [] if Recommendable.redis.zcard(unioned_predictions) == 0
         | 
| 366 352 |  | 
| 367 353 | 
             
                    recommendations = Recommendable.redis.zrevrange(unioned_predictions, 0, options[:count]).map do |object|
         | 
| 368 354 | 
             
                      klass, id = object.split(":")
         | 
| 369 355 | 
             
                      klass.constantize.find(id)
         | 
| 370 356 | 
             
                    end
         | 
| 371 357 |  | 
| 372 | 
            -
                    Recommendable.redis.del  | 
| 373 | 
            -
                    return recommendations
         | 
| 358 | 
            +
                    Recommendable.redis.del(unioned_predictions) and return recommendations
         | 
| 374 359 | 
             
                  end
         | 
| 375 360 |  | 
| 376 361 | 
             
                  # Get a list of recommendations for self on a single recommendable type.
         | 
| @@ -385,16 +370,16 @@ module Recommendable | |
| 385 370 | 
             
                    defaults = { :count => 10 }
         | 
| 386 371 | 
             
                    options = defaults.merge options
         | 
| 387 372 |  | 
| 388 | 
            -
                     | 
| 389 | 
            -
                    return recommendations if likes_for(klass).count + dislikes_for(klass).count == 0 || Recommendable.redis.zcard(predictions_set_for(klass)) == 0
         | 
| 373 | 
            +
                    return [] if likes_for(klass).count + dislikes_for(klass).count == 0 || Recommendable.redis.zcard(predictions_set_for(klass)) == 0
         | 
| 390 374 |  | 
| 375 | 
            +
                    recommendations = []
         | 
| 391 376 | 
             
                    i = 0
         | 
| 392 377 | 
             
                    until recommendations.size == options[:count]
         | 
| 393 378 | 
             
                      prediction = Recommendable.redis.zrevrange(predictions_set_for(klass), i, i).first
         | 
| 394 379 | 
             
                      return recommendations unless prediction # User might not have enough recommendations to return
         | 
| 395 380 |  | 
| 396 381 | 
             
                      object = klassify(klass).find(prediction.split(":")[1])
         | 
| 397 | 
            -
                      recommendations << object | 
| 382 | 
            +
                      recommendations << object
         | 
| 398 383 | 
             
                      i += 1
         | 
| 399 384 | 
             
                    end
         | 
| 400 385 |  | 
| @@ -414,7 +399,7 @@ module Recommendable | |
| 414 399 | 
             
                  #
         | 
| 415 400 | 
             
                  # @param [Object] object the object to fetch the probability for
         | 
| 416 401 | 
             
                  # @return [Float] the likelihood of self disliking the passed object
         | 
| 417 | 
            -
                  # @see # | 
| 402 | 
            +
                  # @see {#probability_of_liking}
         | 
| 418 403 | 
             
                  def probability_of_disliking(object)
         | 
| 419 404 | 
             
                    -probability_of_liking(object)
         | 
| 420 405 | 
             
                  end
         | 
| @@ -434,7 +419,7 @@ module Recommendable | |
| 434 419 | 
             
                    rater.create_recommended_to_sets
         | 
| 435 420 | 
             
                    agreements = common_likes_with(rater, :return_records => false).size
         | 
| 436 421 | 
             
                    agreements += common_dislikes_with(rater, :return_records => false).size
         | 
| 437 | 
            -
                    disagreements = disagreements_with(rater).size
         | 
| 422 | 
            +
                    disagreements = disagreements_with(rater, :return_records => false).size
         | 
| 438 423 |  | 
| 439 424 | 
             
                    similarity = (agreements - disagreements).to_f / (likes.count + dislikes.count)
         | 
| 440 425 | 
             
                    rater.destroy_recommended_to_sets
         | 
| @@ -462,7 +447,7 @@ module Recommendable | |
| 462 447 | 
             
                        in_common = Recommendable.redis.sinter(likes_set_for(klass), rater.likes_set_for(klass))
         | 
| 463 448 |  | 
| 464 449 | 
             
                        if options[:return_records]
         | 
| 465 | 
            -
                          klassify(klass).find in_common | 
| 450 | 
            +
                          klassify(klass).find in_common
         | 
| 466 451 | 
             
                        else
         | 
| 467 452 | 
             
                          in_common.map {|id| "#{klassify(klass)}:#{id}"}
         | 
| 468 453 | 
             
                        end
         | 
| @@ -491,7 +476,7 @@ module Recommendable | |
| 491 476 | 
             
                        in_common = Recommendable.redis.sinter(dislikes_set_for(klass), rater.dislikes_set_for(klass))
         | 
| 492 477 |  | 
| 493 478 | 
             
                        if options[:return_records]
         | 
| 494 | 
            -
                          klassify(klass).find in_common | 
| 479 | 
            +
                          klassify(klass).find in_common
         | 
| 495 480 | 
             
                        else
         | 
| 496 481 | 
             
                          in_common.map {|id| "#{klassify(klass)}:#{id}"}
         | 
| 497 482 | 
             
                        end
         | 
| @@ -562,13 +547,14 @@ module Recommendable | |
| 562 547 | 
             
                  # other users. This is called in the Resque job to refresh recommendations.
         | 
| 563 548 | 
             
                  #
         | 
| 564 549 | 
             
                  # @private
         | 
| 565 | 
            -
                  def update_similarities
         | 
| 550 | 
            +
                  def update_similarities(rater_ids = nil)
         | 
| 566 551 | 
             
                    return unless has_rated_anything?
         | 
| 567 552 | 
             
                    create_recommended_to_sets
         | 
| 553 | 
            +
                    rater_ids ||= Recommendable.user_class.select(:id).map!(&:id)
         | 
| 568 554 |  | 
| 569 | 
            -
                    Recommendable.user_class. | 
| 555 | 
            +
                    Recommendable.user_class.find(rater_ids).each do |rater|
         | 
| 570 556 | 
             
                      next if self == rater || !rater.can_rate?
         | 
| 571 | 
            -
                      Recommendable.redis.zadd similarity_set, similarity_with(rater),  | 
| 557 | 
            +
                      Recommendable.redis.zadd similarity_set, similarity_with(rater), rater.id
         | 
| 572 558 | 
             
                    end
         | 
| 573 559 |  | 
| 574 560 | 
             
                    destroy_recommended_to_sets
         | 
| @@ -644,6 +630,21 @@ module Recommendable | |
| 644 630 | 
             
                    end
         | 
| 645 631 | 
             
                  end
         | 
| 646 632 |  | 
| 633 | 
            +
                  # Used internally during liking/disliking/stashing/ignoring objects. This
         | 
| 634 | 
            +
                  # will prep an object to be liked, disliked, etc. by making sure that self
         | 
| 635 | 
            +
                  # doesn't already have this item in their list of likes, dislikes, stashed
         | 
| 636 | 
            +
                  # items or ignored items.
         | 
| 637 | 
            +
                  #
         | 
| 638 | 
            +
                  # param [Object] object the object to destroy Recommendable models for
         | 
| 639 | 
            +
                  # @private
         | 
| 640 | 
            +
                  def completely_unrecommend(object)
         | 
| 641 | 
            +
                    unlike(object)
         | 
| 642 | 
            +
                    undislike(object)
         | 
| 643 | 
            +
                    unstash(object)
         | 
| 644 | 
            +
                    unignore(object)
         | 
| 645 | 
            +
                    unpredict(object)
         | 
| 646 | 
            +
                  end
         | 
| 647 | 
            +
             | 
| 647 648 | 
             
                  protected :likes_set_for, :dislikes_set_for, :create_recommended_to_sets,
         | 
| 648 649 | 
             
                            :destroy_recommended_to_sets
         | 
| 649 650 |  | 
| @@ -2,5 +2,14 @@ module Recommendable | |
| 2 2 | 
             
              class Railtie < Rails::Railtie
         | 
| 3 3 | 
             
                ActiveRecord::Base.send :include, Recommendable::ActsAsRecommendedTo
         | 
| 4 4 | 
             
                ActiveRecord::Base.send :include, Recommendable::ActsAsRecommendable
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                # Force load models if in a non-development environment and not caching classes
         | 
| 7 | 
            +
                config.after_initialize do |app|
         | 
| 8 | 
            +
                  force_load_models if !Rails.env.development? && !app.config.cache_classes
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def self.force_load_models
         | 
| 12 | 
            +
                  Dir["#{ Rails.root }/app/models/**/*.rb"].each { |m| load m }
         | 
| 13 | 
            +
                end
         | 
| 5 14 | 
             
              end
         | 
| 6 15 | 
             
            end
         | 
    
        data/recommendable.gemspec
    CHANGED
    
    | @@ -4,7 +4,7 @@ require File.expand_path('lib/recommendable/version') | |
| 4 4 | 
             
            Gem::Specification.new do |s|
         | 
| 5 5 | 
             
              s.name = "recommendable"
         | 
| 6 6 | 
             
              s.version = Recommendable::VERSION
         | 
| 7 | 
            -
              s.date =  | 
| 7 | 
            +
              s.date = Time.now.strftime('%Y-%m-%d')
         | 
| 8 8 |  | 
| 9 9 | 
             
              s.authors = ["David Celis"]
         | 
| 10 10 | 
             
              s.email = "david@davidcelis.com"
         | 
| Binary file | 
| @@ -35,8 +35,8 @@ class UserBenchmarkSpec < MiniTest::Unit::TestCase | |
| 35 35 | 
             
                      @users.sample.send(@actions.sample, m)
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 | 
            -
                    @user.update_similarities
         | 
| 39 | 
            -
                    @user.update_recommendations
         | 
| 38 | 
            +
                    @user.send :update_similarities
         | 
| 39 | 
            +
                    @user.send :update_recommendations
         | 
| 40 40 | 
             
                    Recommendable.redis.flushdb
         | 
| 41 41 |  | 
| 42 42 | 
             
                    User.delete_all
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: recommendable
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.1. | 
| 4 | 
            +
              version: 0.1.3
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,11 +9,11 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012- | 
| 12 | 
            +
            date: 2012-02-09 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: sqlite3
         | 
| 16 | 
            -
              requirement: & | 
| 16 | 
            +
              requirement: &70109049948340 !ruby/object:Gem::Requirement
         | 
| 17 17 | 
             
                none: false
         | 
| 18 18 | 
             
                requirements:
         | 
| 19 19 | 
             
                - - ! '>='
         | 
| @@ -21,10 +21,10 @@ dependencies: | |
| 21 21 | 
             
                    version: '0'
         | 
| 22 22 | 
             
              type: :development
         | 
| 23 23 | 
             
              prerelease: false
         | 
| 24 | 
            -
              version_requirements: * | 
| 24 | 
            +
              version_requirements: *70109049948340
         | 
| 25 25 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 26 26 | 
             
              name: minitest
         | 
| 27 | 
            -
              requirement: & | 
| 27 | 
            +
              requirement: &70109049964200 !ruby/object:Gem::Requirement
         | 
| 28 28 | 
             
                none: false
         | 
| 29 29 | 
             
                requirements:
         | 
| 30 30 | 
             
                - - ! '>='
         | 
| @@ -32,10 +32,10 @@ dependencies: | |
| 32 32 | 
             
                    version: '0'
         | 
| 33 33 | 
             
              type: :development
         | 
| 34 34 | 
             
              prerelease: false
         | 
| 35 | 
            -
              version_requirements: * | 
| 35 | 
            +
              version_requirements: *70109049964200
         | 
| 36 36 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 37 37 | 
             
              name: shoulda
         | 
| 38 | 
            -
              requirement: & | 
| 38 | 
            +
              requirement: &70109049963440 !ruby/object:Gem::Requirement
         | 
| 39 39 | 
             
                none: false
         | 
| 40 40 | 
             
                requirements:
         | 
| 41 41 | 
             
                - - ! '>='
         | 
| @@ -43,10 +43,10 @@ dependencies: | |
| 43 43 | 
             
                    version: '0'
         | 
| 44 44 | 
             
              type: :development
         | 
| 45 45 | 
             
              prerelease: false
         | 
| 46 | 
            -
              version_requirements: * | 
| 46 | 
            +
              version_requirements: *70109049963440
         | 
| 47 47 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 48 | 
             
              name: yard
         | 
| 49 | 
            -
              requirement: & | 
| 49 | 
            +
              requirement: &70109049962620 !ruby/object:Gem::Requirement
         | 
| 50 50 | 
             
                none: false
         | 
| 51 51 | 
             
                requirements:
         | 
| 52 52 | 
             
                - - ~>
         | 
| @@ -54,10 +54,10 @@ dependencies: | |
| 54 54 | 
             
                    version: 0.6.0
         | 
| 55 55 | 
             
              type: :development
         | 
| 56 56 | 
             
              prerelease: false
         | 
| 57 | 
            -
              version_requirements: * | 
| 57 | 
            +
              version_requirements: *70109049962620
         | 
| 58 58 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 59 59 | 
             
              name: bundler
         | 
| 60 | 
            -
              requirement: & | 
| 60 | 
            +
              requirement: &70109049961560 !ruby/object:Gem::Requirement
         | 
| 61 61 | 
             
                none: false
         | 
| 62 62 | 
             
                requirements:
         | 
| 63 63 | 
             
                - - ~>
         | 
| @@ -65,10 +65,10 @@ dependencies: | |
| 65 65 | 
             
                    version: 1.0.0
         | 
| 66 66 | 
             
              type: :development
         | 
| 67 67 | 
             
              prerelease: false
         | 
| 68 | 
            -
              version_requirements: * | 
| 68 | 
            +
              version_requirements: *70109049961560
         | 
| 69 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 70 | 
             
              name: jeweler
         | 
| 71 | 
            -
              requirement: & | 
| 71 | 
            +
              requirement: &70109049959600 !ruby/object:Gem::Requirement
         | 
| 72 72 | 
             
                none: false
         | 
| 73 73 | 
             
                requirements:
         | 
| 74 74 | 
             
                - - ~>
         | 
| @@ -76,10 +76,10 @@ dependencies: | |
| 76 76 | 
             
                    version: 1.6.4
         | 
| 77 77 | 
             
              type: :development
         | 
| 78 78 | 
             
              prerelease: false
         | 
| 79 | 
            -
              version_requirements: * | 
| 79 | 
            +
              version_requirements: *70109049959600
         | 
| 80 80 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 81 81 | 
             
              name: rcov
         | 
| 82 | 
            -
              requirement: & | 
| 82 | 
            +
              requirement: &70109049958960 !ruby/object:Gem::Requirement
         | 
| 83 83 | 
             
                none: false
         | 
| 84 84 | 
             
                requirements:
         | 
| 85 85 | 
             
                - - ! '>='
         | 
| @@ -87,10 +87,10 @@ dependencies: | |
| 87 87 | 
             
                    version: '0'
         | 
| 88 88 | 
             
              type: :development
         | 
| 89 89 | 
             
              prerelease: false
         | 
| 90 | 
            -
              version_requirements: * | 
| 90 | 
            +
              version_requirements: *70109049958960
         | 
| 91 91 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 92 92 | 
             
              name: rails
         | 
| 93 | 
            -
              requirement: & | 
| 93 | 
            +
              requirement: &70109049957640 !ruby/object:Gem::Requirement
         | 
| 94 94 | 
             
                none: false
         | 
| 95 95 | 
             
                requirements:
         | 
| 96 96 | 
             
                - - ! '>='
         | 
| @@ -98,10 +98,10 @@ dependencies: | |
| 98 98 | 
             
                    version: 3.0.0
         | 
| 99 99 | 
             
              type: :runtime
         | 
| 100 100 | 
             
              prerelease: false
         | 
| 101 | 
            -
              version_requirements: * | 
| 101 | 
            +
              version_requirements: *70109049957640
         | 
| 102 102 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 103 103 | 
             
              name: redis
         | 
| 104 | 
            -
              requirement: & | 
| 104 | 
            +
              requirement: &70109049971980 !ruby/object:Gem::Requirement
         | 
| 105 105 | 
             
                none: false
         | 
| 106 106 | 
             
                requirements:
         | 
| 107 107 | 
             
                - - ~>
         | 
| @@ -109,10 +109,10 @@ dependencies: | |
| 109 109 | 
             
                    version: 2.2.0
         | 
| 110 110 | 
             
              type: :runtime
         | 
| 111 111 | 
             
              prerelease: false
         | 
| 112 | 
            -
              version_requirements: * | 
| 112 | 
            +
              version_requirements: *70109049971980
         | 
| 113 113 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 114 114 | 
             
              name: resque
         | 
| 115 | 
            -
              requirement: & | 
| 115 | 
            +
              requirement: &70109049971120 !ruby/object:Gem::Requirement
         | 
| 116 116 | 
             
                none: false
         | 
| 117 117 | 
             
                requirements:
         | 
| 118 118 | 
             
                - - ~>
         | 
| @@ -120,10 +120,10 @@ dependencies: | |
| 120 120 | 
             
                    version: 1.19.0
         | 
| 121 121 | 
             
              type: :runtime
         | 
| 122 122 | 
             
              prerelease: false
         | 
| 123 | 
            -
              version_requirements: * | 
| 123 | 
            +
              version_requirements: *70109049971120
         | 
| 124 124 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 125 125 | 
             
              name: resque-loner
         | 
| 126 | 
            -
              requirement: & | 
| 126 | 
            +
              requirement: &70109049969980 !ruby/object:Gem::Requirement
         | 
| 127 127 | 
             
                none: false
         | 
| 128 128 | 
             
                requirements:
         | 
| 129 129 | 
             
                - - ~>
         | 
| @@ -131,7 +131,7 @@ dependencies: | |
| 131 131 | 
             
                    version: 1.2.0
         | 
| 132 132 | 
             
              type: :runtime
         | 
| 133 133 | 
             
              prerelease: false
         | 
| 134 | 
            -
              version_requirements: * | 
| 134 | 
            +
              version_requirements: *70109049969980
         | 
| 135 135 | 
             
            description: Allow a model (typically User) to Like and/or Dislike models in your
         | 
| 136 136 | 
             
              app. Generate recommendations quickly using redis.
         | 
| 137 137 | 
             
            email: david@davidcelis.com
         | 
| @@ -141,17 +141,18 @@ extra_rdoc_files: [] | |
| 141 141 | 
             
            files:
         | 
| 142 142 | 
             
            - .gitignore
         | 
| 143 143 | 
             
            - .travis.yml
         | 
| 144 | 
            +
            - CHANGELOG.markdown
         | 
| 144 145 | 
             
            - Gemfile
         | 
| 145 146 | 
             
            - Gemfile.lock
         | 
| 146 147 | 
             
            - LICENSE.txt
         | 
| 147 148 | 
             
            - README.markdown
         | 
| 148 149 | 
             
            - Rakefile
         | 
| 149 150 | 
             
            - TODO
         | 
| 150 | 
            -
            - app/jobs/recommendable/recommendation_refresher.rb
         | 
| 151 151 | 
             
            - app/models/recommendable/dislike.rb
         | 
| 152 152 | 
             
            - app/models/recommendable/ignore.rb
         | 
| 153 153 | 
             
            - app/models/recommendable/like.rb
         | 
| 154 154 | 
             
            - app/models/recommendable/stashed_item.rb
         | 
| 155 | 
            +
            - app/workers/recommendable/recommendation_refresher.rb
         | 
| 155 156 | 
             
            - config/routes.rb
         | 
| 156 157 | 
             
            - db/migrate/20120124193723_create_likes.rb
         | 
| 157 158 | 
             
            - db/migrate/20120124193728_create_dislikes.rb
         |