neutral 0.0.1
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.
- 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
|