Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Allow Self Love with Thumbs Up #65

Merged
merged 4 commits into from

2 participants

@pboling

Where is the love?

I have Users that need to vote on other Users (hypothetically, of course).

class User
  acts_as_voter     # People vote ...
  acts_as_voteable  # On other people
end

This adds two has_many relationships to the class with the second obscuring the first:

  has_many :votes, :as => :voter, :dependent => :destroy
  has_many :votes, :as => :voteable, :dependent => :destroy

With this pull request the relationship name is configurable, though by default stays as :votes for both, so it is fully backward compatible. Now you can do the following in an initializer:

ThumbsUp.configure do |config|
  config.voteable_relationship_name = :votes_by
  config.voter_relationship_name    = :votes_on
end

Now on any given user:

u = User.first
u.votes_on # => the votes that have been cast on this user
u.votes_by # => the votes that have been cast by this user

Thanks for the great voting tool!

Backwards Compatability

  • 100%
  • Didn't even need to alter any test code
  • Defaults preserve the old behavior

Tests

  • They all pass
  • I updated the README instructions with what I had to do to get it running on latest postgres, and latest mysql
@bouchard bouchard merged commit 8035f07 into from
@bouchard
Owner

This is a very well written pull request, thanks for contributing! Merged.

@pboling

I missed another typo. I re-used the configuration I added to I18n::Airbrake as the template - and I miseed an I18n in a comment. :( Submitted another PR with typo fix.

@pboling

@bouchard - I have realized that, although the primary relationship name change is working fine here, the votes method is used directly in most of the methods in ActsAsVoteable. I need to re-direct those calls to the dynamic _votes_by which will call the configured relationship. Once I do that, this will be fully working.

As long as people don't use this new configuration yet this merge won't cause any issues. But the new configurable relationship names are not fully working yet. I'll fix it soon and send another PR.

@bouchard
Owner
@pboling

Yep!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
34 README.md
@@ -142,14 +142,32 @@ You can also use `--unique-voting false` when running the generator command:
Testing is a bit more than trivial now as our #tally and #plusminus_tally queries don't function properly under SQLite. To set up for testing:
-```
-$ mysql -uroot # You may have set a password locally. Change as needed.
- > GRANT ALL PRIVILEGES ON 'thumbs_up_test' to 'test'@'localhost' IDENTIFIED BY 'test';
- > CREATE DATABASE 'thumbs_up_test';
- > exit;
-
-$ rake # Runs the test suite.
-```
+* mysql
+
+ ```
+ $ mysql -uroot # You may have set a password locally. Change as needed.
+ > CREATE USER 'test'@'localhost' IDENTIFIED BY 'test';
+ > CREATE DATABASE thumbs_up_test;
+ > USE thumbs_up_test;
+ > GRANT ALL PRIVILEGES ON thumbs_up_test TO 'test'@'localhost' IDENTIFIED BY 'test';
+ > exit;
+ ```
+* Postgres
+
+ ```
+ $ psql # You may have set a password locally. Change as needed.
+ > CREATE ROLE test;
+ > ALTER ROLE test WITH SUPERUSER;
+ > ALTER ROLE test WITH LOGIN;
+ > CREATE DATABASE thumbs_up_test;
+ > GRANT ALL PRIVILEGES ON DATABASE thumbs_up_test to test;
+ > \q
+ ```
+* Run tests
+
+ ```
+ $ rake # Runs the test suite against all adapters.
+ ```
Credits
=======
View
2  Rakefile
@@ -40,4 +40,4 @@ task :test_all_databases do
Rake::Task['test'].execute
end
-task :default => :test_all_databases
+task :default => :test_all_databases
View
20 lib/acts_as_voteable.rb
@@ -8,7 +8,10 @@ def self.included(base)
module ClassMethods
def acts_as_voteable
- has_many :votes, :as => :voteable, :dependent => :destroy
+ has_many ThumbsUp.configuration[:voteable_relationship_name],
+ :as => :voteable,
+ :dependent => :destroy,
+ :class_name => "Vote"
include ThumbsUp::ActsAsVoteable::InstanceMethods
extend ThumbsUp::ActsAsVoteable::SingletonMethods
@@ -61,20 +64,25 @@ def column_names_for_tally
module InstanceMethods
+ # wraps the dynamic, configured, relationship name
+ def _votes_by
+ self.send(ThumbsUp.configuration[:voteable_relationship_name])
+ end
+
def votes_for
- self.votes.where(:vote => true).count
+ self._votes_by.where(:vote => true).count
end
def votes_against
- self.votes.where(:vote => false).count
+ self._votes_by.where(:vote => false).count
end
def percent_for
- (votes_for.to_f * 100 / (self.votes.size + 0.0001)).round
+ (votes_for.to_f * 100 / (self._votes_by.size + 0.0001)).round
end
def percent_against
- (votes_against.to_f * 100 / (self.votes.size + 0.0001)).round
+ (votes_against.to_f * 100 / (self._votes_by.size + 0.0001)).round
end
# You'll probably want to use this method to display how 'good' a particular voteable
@@ -113,7 +121,7 @@ def voters_who_voted_for
def voters_who_voted_against
votes.where(:vote => false).map(&:voter).uniq
end
-
+
def voted_by?(voter)
0 < Vote.where(
:voteable_id => self.id,
View
12 lib/acts_as_voter.rb
@@ -12,8 +12,11 @@ def acts_as_voter
# If you want to nullify (and keep the votes), you'll need to remove
# the unique constraint on the [ voter, voteable ] index in the database.
# has_many :votes, :as => :voter, :dependent => :nullify
- # Destroy votes when a user is deleted.
- has_many :votes, :as => :voter, :dependent => :destroy
+ # Destroy voter's votes when the voter is deleted.
+ has_many ThumbsUp.configuration[:voter_relationship_name],
+ :as => :voter,
+ :dependent => :destroy,
+ :class_name => "Vote"
include ThumbsUp::ActsAsVoter::InstanceMethods
extend ThumbsUp::ActsAsVoter::SingletonMethods
@@ -27,6 +30,11 @@ module SingletonMethods
# This module contains instance methods
module InstanceMethods
+ # wraps the dynamic, configured, relationship name
+ def _votes_on
+ self.send(ThumbsUp.configuration[:voteable_relationship_name])
+ end
+
# Usage user.vote_count(:up) # All +1 votes
# user.vote_count(:down) # All -1 votes
# user.vote_count() # All votes
View
29 lib/thumbs_up.rb
@@ -1,17 +1,36 @@
require 'acts_as_voteable'
require 'acts_as_voter'
require 'has_karma'
+require 'thumbs_up/configuration'
+require 'thumbs_up/base'
+require 'thumbs_up/version'
module ThumbsUp
- module Base
- def quoted_true
- ActiveRecord::Base.connection.quoted_true
+
+ class << self
+
+ # An ThumbsUp::Configuration object. Must act like a hash and return sensible
+ # values for all ThumbsUp::Configuration::OPTIONS. See ThumbsUp::Configuration.
+ attr_writer :configuration
+
+ # Call this method to modify defaults in your initializers.
+ #
+ # @example
+ # ThumbsUp.configure do |config|
+ # config.voteable_relationship_name = :votes_by
+ # config.voter_relationship_name = :votes_on
+ # end
+ def configure
+ yield(configuration)
end
- def quoted_false
- ActiveRecord::Base.connection.quoted_false
+ # The configuration object.
+ # @see I18::Airbrake.configure
+ def configuration
+ @configuration ||= Configuration.new
end
end
+
end
ActiveRecord::Base.send(:include, ThumbsUp::ActsAsVoteable)
View
11 lib/thumbs_up/base.rb
@@ -0,0 +1,11 @@
+module ThumbsUp
+ module Base
+ def quoted_true
+ ActiveRecord::Base.connection.quoted_true
+ end
+
+ def quoted_false
+ ActiveRecord::Base.connection.quoted_false
+ end
+ end
+end
View
51 lib/thumbs_up/configuration.rb
@@ -0,0 +1,51 @@
+module ThumbsUp
+ class Configuration
+
+ OPTIONS = [:voteable_relationship_name, :voter_relationship_name].freeze
+
+ # Specify the name of the relationship from voted on things to voters.
+ # Default is votes
+ # In order to have a model that votes on itself,
+ # e.g. Users vote on Users,
+ # must change :voteable_relationship_name or :voter_relationship_name
+ # to a non-default value
+ attr_accessor :voteable_relationship_name
+
+ # Specify the name of the relationship from voters to voted on things
+ # Default is votes
+ # In order to have a model that votes on itself,
+ # e.g. Users vote on Users,
+ # must change :voteable_relationship_name or :voter_relationship_name
+ # to a non-default value
+ attr_accessor :voter_relationship_name
+
+ def initialize
+ # these defaults can be overridden in the ThumbsUp.config block
+ @voteable_relationship_name = :votes
+ @voter_relationship_name = :votes
+ end
+
+ # Allows config options to be read like a hash
+ #
+ # @param [Symbol] option Key for a given attribute
+ def [](option)
+ send(option)
+ end
+
+ # Returns a hash of all configurable options
+ def to_hash
+ OPTIONS.inject({}) do |hash, option|
+ hash[option.to_sym] = self.send(option)
+ hash
+ end
+ end
+
+ # Returns a hash of all configurable options merged with +hash+
+ #
+ # @param [Hash] hash A set of configuration options that will take precedence over the defaults
+ def merge(hash)
+ to_hash.merge(hash)
+ end
+
+ end
+end
Something went wrong with that request. Please try again.