neutral 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +194 -0
- data/MIT-LICENSE +20 -0
- data/README.md +143 -0
- data/Rakefile +21 -0
- data/app/assets/stylesheets/neutral/index.css +43 -0
- data/app/controllers/neutral/application_controller.rb +22 -0
- data/app/controllers/neutral/votes_controller.rb +48 -0
- data/app/helpers/neutral/votes_helper.rb +16 -0
- data/app/models/neutral/vote.rb +26 -0
- data/app/models/neutral/voting.rb +39 -0
- data/app/views/neutral/votes/create.js.erb +27 -0
- data/app/views/neutral/votes/destroy.js.erb +18 -0
- data/app/views/neutral/votes/errors/cannot_change.js.erb +9 -0
- data/app/views/neutral/votes/errors/duplicate.js.erb +9 -0
- data/app/views/neutral/votes/errors/require_login.js.erb +10 -0
- data/app/views/neutral/votes/update.js.erb +11 -0
- data/bin/rails +8 -0
- data/config/locales/impartial.yml +6 -0
- data/config/routes.rb +3 -0
- data/lib/generators/neutral/formats.rb +16 -0
- data/lib/generators/neutral/install/install_generator.rb +47 -0
- data/lib/generators/neutral/install/templates/initializer.rb +41 -0
- data/lib/generators/neutral/install/templates/locale.yml +5 -0
- data/lib/generators/neutral/install/templates/votes.rb +12 -0
- data/lib/generators/neutral/install/templates/votings.rb +12 -0
- data/lib/generators/neutral/uninstall/templates/drop_neutral_votes_table.rb +9 -0
- data/lib/generators/neutral/uninstall/templates/drop_neutral_votings_table.rb +9 -0
- data/lib/generators/neutral/uninstall/uninstall_generator.rb +55 -0
- data/lib/neutral/configuration.rb +27 -0
- data/lib/neutral/engine.rb +28 -0
- data/lib/neutral/errors.rb +11 -0
- data/lib/neutral/helpers/action_view_extension.rb +19 -0
- data/lib/neutral/helpers/routes.rb +9 -0
- data/lib/neutral/icons/collection.rb +55 -0
- data/lib/neutral/icons/set.rb +22 -0
- data/lib/neutral/model/active_record_extension.rb +31 -0
- data/lib/neutral/model/vote_cached.rb +32 -0
- data/lib/neutral/version.rb +3 -0
- data/lib/neutral/voting_builder/builder.rb +42 -0
- data/lib/neutral/voting_builder/elements/link.rb +46 -0
- data/lib/neutral/voting_builder/elements/span.rb +41 -0
- data/lib/neutral/voting_builder/elements.rb +23 -0
- data/lib/neutral/voting_builder/router.rb +45 -0
- data/lib/neutral/voting_builder/structure.rb +21 -0
- data/lib/neutral.rb +46 -0
- data/neutral.gemspec +34 -0
- data/spec/controllers/votes_controller_spec.rb +138 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +29 -0
- data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/spec/dummy/app/controllers/application_controller.rb +11 -0
- data/spec/dummy/app/controllers/posts_controller.rb +33 -0
- data/spec/dummy/app/controllers/sessions_controller.rb +21 -0
- data/spec/dummy/app/controllers/users_controller.rb +24 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/models/user.rb +7 -0
- data/spec/dummy/app/views/layouts/application.html.erb +25 -0
- data/spec/dummy/app/views/posts/_form.html.erb +21 -0
- data/spec/dummy/app/views/posts/index.html.erb +26 -0
- data/spec/dummy/app/views/posts/new.html.erb +5 -0
- data/spec/dummy/app/views/sessions/new.html.erb +18 -0
- data/spec/dummy/app/views/users/_form.html.erb +25 -0
- data/spec/dummy/app/views/users/new.html.erb +3 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/neutral.rb +41 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/locales/neutral.yml +5 -0
- data/spec/dummy/config/routes.rb +13 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20140110214145_create_posts.rb +9 -0
- data/spec/dummy/db/migrate/20140111225442_create_users.rb +10 -0
- data/spec/dummy/db/migrate/20140118172808881589_create_neutral_votes.rb +12 -0
- data/spec/dummy/db/migrate/20140118172808882161_create_neutral_votings.rb +12 -0
- data/spec/dummy/db/schema.rb +47 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories.rb +18 -0
- data/spec/helpers/action_view_extension_spec.rb +45 -0
- data/spec/icons/collection_spec.rb +38 -0
- data/spec/icons/set_spec.rb +25 -0
- data/spec/models/active_record_extension_spec.rb +49 -0
- data/spec/models/vote_spec.rb +51 -0
- data/spec/models/voting_spec.rb +60 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/shared_examples/controller_examples.rb +45 -0
- data/spec/support/votes_controller_base_class.rb +28 -0
- data/spec/support/votes_controller_helpers.rb +24 -0
- data/spec/voting_builder/builder_spec.rb +66 -0
- data/spec/voting_builder/elements/link_spec.rb +31 -0
- data/spec/voting_builder/elements/span_spec.rb +44 -0
- data/spec/voting_builder/router_spec.rb +57 -0
- data/spec/voting_builder/structure_spec.rb +59 -0
- metadata +449 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
$(document).ready(function() {
|
2
|
+
|
3
|
+
neutral = $("div.neutral").find("<%= by_url %>").parent();
|
4
|
+
|
5
|
+
neutral.find("span#positive").html("<%= voting.positive.nonzero? %>");
|
6
|
+
neutral.find("span#negative").html("<%= voting.negative.nonzero? %>");
|
7
|
+
neutral.find("span#difference").html("<%= voting.difference.nonzero?.try(:abs) %>");
|
8
|
+
|
9
|
+
neutral.find("span#difference").attr("class", "<%= voting.difference > 0 ? 'positive' : 'negative' %>");
|
10
|
+
|
11
|
+
});
|
data/bin/rails
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/neutral/engine', __FILE__)
|
6
|
+
|
7
|
+
require 'rails/all'
|
8
|
+
require 'rails/engine/commands'
|
data/config/routes.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Neutral
|
2
|
+
module Formats
|
3
|
+
def css_format
|
4
|
+
return [css_manifest('.css'), ' *='] if File.exist? css_manifest('.css')
|
5
|
+
return [css_manifest('.css.sass'), ' //='] if File.exists? css_manifest('.css.sass')
|
6
|
+
return [css_manifest('.sass'), ' //='] if File.exist? css_manifest('.sass')
|
7
|
+
return [css_manifest('.css.scss'), ' //='] if File.exist? css_manifest('.css.scss')
|
8
|
+
return [css_manifest('.scss'), ' //='] if File.exist? css_manifest('.scss')
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def css_manifest(extension)
|
13
|
+
"app/assets/stylesheets/application#{extension}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rails/generators/migration'
|
2
|
+
require File.expand_path('../../formats', __FILE__)
|
3
|
+
|
4
|
+
module Neutral
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < ::Rails::Generators::Base
|
7
|
+
include ::Rails::Generators::Migration
|
8
|
+
include Neutral::Formats
|
9
|
+
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
11
|
+
|
12
|
+
def migrations
|
13
|
+
migration_template "votes.rb", "db/migrate/create_neutral_votes"
|
14
|
+
migration_template "votings.rb", "db/migrate/create_neutral_votings"
|
15
|
+
end
|
16
|
+
|
17
|
+
def routes
|
18
|
+
route "neutral"
|
19
|
+
end
|
20
|
+
|
21
|
+
def locale
|
22
|
+
template "locale.yml", "config/locales/neutral.yml"
|
23
|
+
end
|
24
|
+
|
25
|
+
def stylesheet
|
26
|
+
if File.binread(css_format[0]).include? "require neutral"
|
27
|
+
say_status "skipped", "insert into '#{css_format[0]}'", :yellow
|
28
|
+
else
|
29
|
+
insert_into_file css_format[0], "\n#{css_format[1]} require neutral\n", after: /require_self/
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initializer
|
34
|
+
template "initializer.rb", "config/initializers/neutral.rb"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def self.next_migration_number(dirname)
|
39
|
+
if ActiveRecord::Base.timestamped_migrations
|
40
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S%6N")
|
41
|
+
else
|
42
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#Neutral engine Initializer
|
2
|
+
|
3
|
+
Neutral.configure do |config|
|
4
|
+
# Set to false wheter a voter cannot change(i.e. update or delete) his vote.
|
5
|
+
#config.can_change = true
|
6
|
+
|
7
|
+
# Configure method providing a voter instance.
|
8
|
+
# Also make sure your current voter method is accessible from your ApplicationController
|
9
|
+
# as a helper_method.
|
10
|
+
#config.current_voter_method = :current_user
|
11
|
+
|
12
|
+
# Determine wheter a voter is required to be logged in. Setting to false
|
13
|
+
# is not recommended.
|
14
|
+
#config.require_login = true
|
15
|
+
|
16
|
+
# Define a vote owner class(usually User, Client, Customer)
|
17
|
+
#config.vote_owner_class = 'User'
|
18
|
+
|
19
|
+
# Define default icon set. Used when no explicit icon set for helper
|
20
|
+
# 'voting_for' is passed.
|
21
|
+
#config.default_icon_set = :thumbs
|
22
|
+
end
|
23
|
+
|
24
|
+
# You can also define your custom icon set providing valid FontAwesome icon class definitions for
|
25
|
+
# positive, negative and remove icon. You cannot override icon sets that already exist('thumbs' and 'operations' by default).
|
26
|
+
# Beware of typos and non-existent FontAwesome icon classes.
|
27
|
+
# Then you can used your own icon set by passing its name to the view helper : 'voting_for voteable, icons: :mysupericons'
|
28
|
+
|
29
|
+
# Neutral.define do
|
30
|
+
# set :myicons do
|
31
|
+
# positive "fa-plus-circle"
|
32
|
+
# negative "fa-minus-circle"
|
33
|
+
# remove "fa-times"
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# set :mythumbs do
|
37
|
+
# positive "fa-thumbs-up"
|
38
|
+
# negative "fa-thumbs-down"
|
39
|
+
# remove "fa-times"
|
40
|
+
# end
|
41
|
+
# end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateNeutralVotings < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :neutral_votings do |t|
|
4
|
+
t.string :votingable_type
|
5
|
+
t.integer :votingable_id
|
6
|
+
t.integer :positive, default: 0
|
7
|
+
t.integer :negative, default: 0
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.expand_path('../../formats', __FILE__)
|
2
|
+
|
3
|
+
module Neutral
|
4
|
+
module Generators
|
5
|
+
class UninstallGenerator < ::Rails::Generators::Base
|
6
|
+
include ::Rails::Generators::Migration
|
7
|
+
include Neutral::Formats
|
8
|
+
|
9
|
+
source_root File.expand_path("../templates", __FILE__)
|
10
|
+
|
11
|
+
def remove_route
|
12
|
+
comment_lines "config/routes.rb", /neutral/
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_locale
|
16
|
+
remove_file "config/locales/neutral.yml"
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_stylesheet
|
20
|
+
if File.binread(css_format[0]).include? "require neutral"
|
21
|
+
gsub_file css_format[0], "\n#{css_format[1]} require neutral\n", ""
|
22
|
+
else
|
23
|
+
say_status("skipped", "remove from '#{css_format[0]}'", :yellow)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_database_entities
|
28
|
+
-> {
|
29
|
+
migration_template "drop_neutral_votes_table.rb", "db/migrate/drop_neutral_votes_table"
|
30
|
+
migration_template "drop_neutral_votings_table.rb", "db/migrate/drop_neutral_votings_table"
|
31
|
+
|
32
|
+
rake("db:migrate")
|
33
|
+
|
34
|
+
Dir.glob("db/migrate/*").keep_if { |f| f.include?("neutral") }.each do |file|
|
35
|
+
remove_file(file)
|
36
|
+
end if yes?("Remove also remaining migration files?")
|
37
|
+
|
38
|
+
}.call if yes?("Remove database entities?('neutral_votes' and 'neutral_votings' tables)")
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_initializer
|
42
|
+
remove_file "config/initializers/neutral.rb"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def self.next_migration_number(dirname)
|
47
|
+
if ActiveRecord::Base.timestamped_migrations
|
48
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S%6N")
|
49
|
+
else
|
50
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Neutral
|
2
|
+
def self.config
|
3
|
+
@config ||= Configuration.new
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.configure(&block)
|
7
|
+
yield config if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
class Configuration
|
11
|
+
include ActiveSupport::Configurable
|
12
|
+
|
13
|
+
config_accessor :can_change
|
14
|
+
config_accessor :current_voter_method
|
15
|
+
config_accessor :require_login
|
16
|
+
config_accessor :vote_owner_class
|
17
|
+
config_accessor :default_icon_set
|
18
|
+
end
|
19
|
+
|
20
|
+
configure do |config|
|
21
|
+
config.can_change = true
|
22
|
+
config.current_voter_method = :current_user
|
23
|
+
config.require_login = true
|
24
|
+
config.vote_owner_class = 'User'
|
25
|
+
config.default_icon_set = :thumbs
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Neutral
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace Neutral
|
4
|
+
|
5
|
+
initializer 'extensions' do
|
6
|
+
::ActiveSupport.on_load(:active_record) { extend Neutral::Model::ActiveRecordExtension }
|
7
|
+
::ActiveSupport.on_load(:action_view) { include Neutral::Helpers::ActionViewExtension }
|
8
|
+
::ActionDispatch::Routing::Mapper.send(:include, Neutral::Helpers::Routes)
|
9
|
+
end
|
10
|
+
|
11
|
+
config.after_initialize do
|
12
|
+
::ApplicationController.class_eval do
|
13
|
+
alias_method :current_voter, Neutral.config.current_voter_method
|
14
|
+
helper_method :current_voter
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'neutral/application_controller'
|
18
|
+
end
|
19
|
+
|
20
|
+
config.generators do |g|
|
21
|
+
g.test_framework :rspec, fixture: false
|
22
|
+
g.fixture_replacement :factory_girl, dir: 'spec/factories'
|
23
|
+
g.assets = false
|
24
|
+
g.helpers = false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Neutral
|
2
|
+
module Errors
|
3
|
+
class InvalidVoteableObject < StandardError;end
|
4
|
+
class RequireLogin < StandardError;end
|
5
|
+
class InvalidVoterInstance < StandardError;end
|
6
|
+
class CannotChange < StandardError;end
|
7
|
+
class DuplicateVote < StandardError;end
|
8
|
+
class UndefinedIconSet < StandardError;end
|
9
|
+
class AlreadyDefinedIconSet < StandardError;end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Neutral
|
2
|
+
module Helpers
|
3
|
+
module ActionViewExtension
|
4
|
+
def voting_for(*args)
|
5
|
+
voteable = args.shift
|
6
|
+
options = args.extract_options!.merge(voter: current_voter)
|
7
|
+
|
8
|
+
not_voteable(voteable) unless voteable.is_a?(Neutral::Model::ActiveRecordExtension::Voteable)
|
9
|
+
|
10
|
+
Neutral::VotingBuilder::Builder.new(voteable, options).build
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def not_voteable(voteable)
|
15
|
+
raise Neutral::Errors::InvalidVoteableObject, "#{voteable.inspect} is not a voteable object. Try adding 'neutral' into your object's model class definition."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Neutral
|
2
|
+
module Icons
|
3
|
+
class Collection
|
4
|
+
DEFAULTS = {
|
5
|
+
thumbs: {
|
6
|
+
positive: 'fa-thumbs-o-up',
|
7
|
+
negative: 'fa-thumbs-o-down',
|
8
|
+
remove: 'fa-times'
|
9
|
+
},
|
10
|
+
operations: {
|
11
|
+
positive: 'fa-plus-circle',
|
12
|
+
negative: 'fa-minus-circle',
|
13
|
+
remove: 'fa-times'
|
14
|
+
}
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
DEFAULTS.each do |name, definitions|
|
19
|
+
define!(name, definitions)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(set)
|
24
|
+
already_defined(set.name) if exists? set.name
|
25
|
+
define!(set.name, set.definitions)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
class Definitions < Struct.new(:positive, :negative, :remove)
|
31
|
+
[:positive=, :negative=, :remove=].each { |method| undef_method method }
|
32
|
+
end
|
33
|
+
|
34
|
+
def define!(name, definitions)
|
35
|
+
class_eval do
|
36
|
+
define_method(name) do
|
37
|
+
Definitions.new definitions[:positive], definitions[:negative], definitions[:remove]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def exists?(name)
|
43
|
+
respond_to? name
|
44
|
+
end
|
45
|
+
|
46
|
+
def already_defined(name)
|
47
|
+
raise Neutral::Errors::AlreadyDefinedIconSet, "Icon set '#{name}' is already defined"
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(name)
|
51
|
+
raise Neutral::Errors::UndefinedIconSet, "Icon set '#{name}' is not defined"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Neutral
|
2
|
+
module Icons
|
3
|
+
class Set
|
4
|
+
attr_reader :definitions
|
5
|
+
def initialize(name, &block)
|
6
|
+
@name = name
|
7
|
+
@definitions = Neutral.icons.send(Neutral.config.default_icon_set).to_h
|
8
|
+
instance_eval(&block) if block_given?
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
[:positive, :negative, :remove].each do |icon|
|
16
|
+
define_method(icon) do |definition|
|
17
|
+
definitions[icon] = definition.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Neutral
|
2
|
+
module Model
|
3
|
+
module ActiveRecordExtension
|
4
|
+
def neutral
|
5
|
+
include Voteable
|
6
|
+
end
|
7
|
+
|
8
|
+
def neutral_voter
|
9
|
+
include Voter
|
10
|
+
end
|
11
|
+
|
12
|
+
module Voteable
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
included do
|
16
|
+
has_many :votes, as: :voteable, class_name: Neutral::Vote, dependent: :destroy
|
17
|
+
has_one :voting, as: :votingable, class_name: Neutral::Voting, dependent: :destroy
|
18
|
+
has_many :voters, through: :votes, source: :voter
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Voter
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
included do
|
26
|
+
has_many :votes_given, class_name: Neutral::Vote, foreign_key: :voter_id
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Neutral
|
2
|
+
module Model
|
3
|
+
module VoteCached
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
delegate :voting, to: :voteable
|
8
|
+
|
9
|
+
after_create :add_vote_to_voting
|
10
|
+
after_update :edit_vote_within_voting
|
11
|
+
after_destroy :remove_vote_from_voting
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_vote_to_voting
|
15
|
+
if voting
|
16
|
+
voting.add_to_existing(nature)
|
17
|
+
else
|
18
|
+
Voting.init(self)
|
19
|
+
clear_association_cache
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def edit_vote_within_voting
|
24
|
+
voting.edit nature
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_vote_from_voting
|
28
|
+
voting.remove nature
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Neutral
|
2
|
+
module VotingBuilder
|
3
|
+
class Builder
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
include ActionView::Helpers::OutputSafetyHelper
|
6
|
+
include Elements
|
7
|
+
|
8
|
+
attr_reader :voteable, :voter, :icons, :difference
|
9
|
+
def initialize(voteable, options)
|
10
|
+
@voteable = voteable
|
11
|
+
@voter = options[:voter]
|
12
|
+
@icons = options[:icons] || Neutral.config.default_icon_set
|
13
|
+
@difference = options[:difference]
|
14
|
+
end
|
15
|
+
|
16
|
+
def build
|
17
|
+
content_tag :div, elements, class: 'neutral'
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def elements
|
22
|
+
safe_join(structure.map { |element| send(element) })
|
23
|
+
end
|
24
|
+
|
25
|
+
def voting
|
26
|
+
voteable.voting || Neutral::Voting.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def vote
|
30
|
+
@vote ||= voter && voteable.votes.where(voter_id: voter.id).first_or_initialize || voteable.votes.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def router
|
34
|
+
@router ||= Router.new vote
|
35
|
+
end
|
36
|
+
|
37
|
+
def structure
|
38
|
+
Structure.new(vote.persisted?, difference).to_a
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Neutral
|
2
|
+
module VotingBuilder
|
3
|
+
module Elements
|
4
|
+
class Link
|
5
|
+
include ActionView::Helpers::TagHelper
|
6
|
+
include ActionView::Helpers::UrlHelper
|
7
|
+
|
8
|
+
def initialize(router, icon)
|
9
|
+
@router = router
|
10
|
+
@icon = icon
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def fa_icon
|
15
|
+
"fa #{@icon}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def path
|
19
|
+
@router[:path]
|
20
|
+
end
|
21
|
+
|
22
|
+
def verb
|
23
|
+
@router[:method]
|
24
|
+
end
|
25
|
+
|
26
|
+
class Positive < Link
|
27
|
+
def to_s
|
28
|
+
link_to content_tag(:i, nil, class: fa_icon), path, class: 'positive', method: verb, remote: true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Negative < Link
|
33
|
+
def to_s
|
34
|
+
link_to content_tag(:i, nil, class: fa_icon), path, class: 'negative', method: verb, remote: true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Remove < Link
|
39
|
+
def to_s
|
40
|
+
link_to content_tag(:i, nil, class: fa_icon), path, class: 'remove', method: verb, remote: true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Neutral
|
2
|
+
module VotingBuilder
|
3
|
+
module Elements
|
4
|
+
class Span
|
5
|
+
include ActionView::Helpers::TagHelper
|
6
|
+
|
7
|
+
def initialize(total)
|
8
|
+
@total = total
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def total
|
13
|
+
@total.nonzero?
|
14
|
+
end
|
15
|
+
|
16
|
+
class Positive < Span
|
17
|
+
def to_s
|
18
|
+
content_tag :span, total, id: 'positive'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Negative < Span
|
23
|
+
def to_s
|
24
|
+
content_tag :span, total, id: 'negative'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Difference < Span
|
29
|
+
def to_s
|
30
|
+
content_tag :span, total.try(:abs), class: color, id: 'difference'
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def color
|
35
|
+
total > 0 ? 'positive' : 'negative' if total
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'neutral/voting_builder/elements/link'
|
2
|
+
require 'neutral/voting_builder/elements/span'
|
3
|
+
|
4
|
+
module Neutral
|
5
|
+
module VotingBuilder
|
6
|
+
module Elements
|
7
|
+
private
|
8
|
+
|
9
|
+
%w[positive negative difference].each do |span|
|
10
|
+
define_method("#{span}_span") do
|
11
|
+
Span.const_get(span.capitalize).new voting.send(span)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
%w[positive negative remove].each do |link|
|
17
|
+
define_method("#{link}_link") do
|
18
|
+
Link.const_get(link.capitalize).new router[link], Neutral.icons.send(icons).send(link)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|