neutral 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +194 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +143 -0
  7. data/Rakefile +21 -0
  8. data/app/assets/stylesheets/neutral/index.css +43 -0
  9. data/app/controllers/neutral/application_controller.rb +22 -0
  10. data/app/controllers/neutral/votes_controller.rb +48 -0
  11. data/app/helpers/neutral/votes_helper.rb +16 -0
  12. data/app/models/neutral/vote.rb +26 -0
  13. data/app/models/neutral/voting.rb +39 -0
  14. data/app/views/neutral/votes/create.js.erb +27 -0
  15. data/app/views/neutral/votes/destroy.js.erb +18 -0
  16. data/app/views/neutral/votes/errors/cannot_change.js.erb +9 -0
  17. data/app/views/neutral/votes/errors/duplicate.js.erb +9 -0
  18. data/app/views/neutral/votes/errors/require_login.js.erb +10 -0
  19. data/app/views/neutral/votes/update.js.erb +11 -0
  20. data/bin/rails +8 -0
  21. data/config/locales/impartial.yml +6 -0
  22. data/config/routes.rb +3 -0
  23. data/lib/generators/neutral/formats.rb +16 -0
  24. data/lib/generators/neutral/install/install_generator.rb +47 -0
  25. data/lib/generators/neutral/install/templates/initializer.rb +41 -0
  26. data/lib/generators/neutral/install/templates/locale.yml +5 -0
  27. data/lib/generators/neutral/install/templates/votes.rb +12 -0
  28. data/lib/generators/neutral/install/templates/votings.rb +12 -0
  29. data/lib/generators/neutral/uninstall/templates/drop_neutral_votes_table.rb +9 -0
  30. data/lib/generators/neutral/uninstall/templates/drop_neutral_votings_table.rb +9 -0
  31. data/lib/generators/neutral/uninstall/uninstall_generator.rb +55 -0
  32. data/lib/neutral/configuration.rb +27 -0
  33. data/lib/neutral/engine.rb +28 -0
  34. data/lib/neutral/errors.rb +11 -0
  35. data/lib/neutral/helpers/action_view_extension.rb +19 -0
  36. data/lib/neutral/helpers/routes.rb +9 -0
  37. data/lib/neutral/icons/collection.rb +55 -0
  38. data/lib/neutral/icons/set.rb +22 -0
  39. data/lib/neutral/model/active_record_extension.rb +31 -0
  40. data/lib/neutral/model/vote_cached.rb +32 -0
  41. data/lib/neutral/version.rb +3 -0
  42. data/lib/neutral/voting_builder/builder.rb +42 -0
  43. data/lib/neutral/voting_builder/elements/link.rb +46 -0
  44. data/lib/neutral/voting_builder/elements/span.rb +41 -0
  45. data/lib/neutral/voting_builder/elements.rb +23 -0
  46. data/lib/neutral/voting_builder/router.rb +45 -0
  47. data/lib/neutral/voting_builder/structure.rb +21 -0
  48. data/lib/neutral.rb +46 -0
  49. data/neutral.gemspec +34 -0
  50. data/spec/controllers/votes_controller_spec.rb +138 -0
  51. data/spec/dummy/Rakefile +6 -0
  52. data/spec/dummy/app/assets/images/.keep +0 -0
  53. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  54. data/spec/dummy/app/assets/stylesheets/application.css +29 -0
  55. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  56. data/spec/dummy/app/controllers/application_controller.rb +11 -0
  57. data/spec/dummy/app/controllers/posts_controller.rb +33 -0
  58. data/spec/dummy/app/controllers/sessions_controller.rb +21 -0
  59. data/spec/dummy/app/controllers/users_controller.rb +24 -0
  60. data/spec/dummy/app/models/.keep +0 -0
  61. data/spec/dummy/app/models/post.rb +3 -0
  62. data/spec/dummy/app/models/user.rb +7 -0
  63. data/spec/dummy/app/views/layouts/application.html.erb +25 -0
  64. data/spec/dummy/app/views/posts/_form.html.erb +21 -0
  65. data/spec/dummy/app/views/posts/index.html.erb +26 -0
  66. data/spec/dummy/app/views/posts/new.html.erb +5 -0
  67. data/spec/dummy/app/views/sessions/new.html.erb +18 -0
  68. data/spec/dummy/app/views/users/_form.html.erb +25 -0
  69. data/spec/dummy/app/views/users/new.html.erb +3 -0
  70. data/spec/dummy/bin/bundle +3 -0
  71. data/spec/dummy/bin/rails +4 -0
  72. data/spec/dummy/bin/rake +4 -0
  73. data/spec/dummy/config/application.rb +29 -0
  74. data/spec/dummy/config/boot.rb +5 -0
  75. data/spec/dummy/config/database.yml +25 -0
  76. data/spec/dummy/config/environment.rb +5 -0
  77. data/spec/dummy/config/environments/development.rb +29 -0
  78. data/spec/dummy/config/environments/production.rb +80 -0
  79. data/spec/dummy/config/environments/test.rb +36 -0
  80. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  81. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  82. data/spec/dummy/config/initializers/neutral.rb +41 -0
  83. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  84. data/spec/dummy/config/initializers/session_store.rb +3 -0
  85. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  86. data/spec/dummy/config/locales/en.yml +23 -0
  87. data/spec/dummy/config/locales/neutral.yml +5 -0
  88. data/spec/dummy/config/routes.rb +13 -0
  89. data/spec/dummy/config.ru +4 -0
  90. data/spec/dummy/db/migrate/20140110214145_create_posts.rb +9 -0
  91. data/spec/dummy/db/migrate/20140111225442_create_users.rb +10 -0
  92. data/spec/dummy/db/migrate/20140118172808881589_create_neutral_votes.rb +12 -0
  93. data/spec/dummy/db/migrate/20140118172808882161_create_neutral_votings.rb +12 -0
  94. data/spec/dummy/db/schema.rb +47 -0
  95. data/spec/dummy/lib/assets/.keep +0 -0
  96. data/spec/dummy/log/.keep +0 -0
  97. data/spec/dummy/public/404.html +58 -0
  98. data/spec/dummy/public/422.html +58 -0
  99. data/spec/dummy/public/500.html +57 -0
  100. data/spec/dummy/public/favicon.ico +0 -0
  101. data/spec/factories.rb +18 -0
  102. data/spec/helpers/action_view_extension_spec.rb +45 -0
  103. data/spec/icons/collection_spec.rb +38 -0
  104. data/spec/icons/set_spec.rb +25 -0
  105. data/spec/models/active_record_extension_spec.rb +49 -0
  106. data/spec/models/vote_spec.rb +51 -0
  107. data/spec/models/voting_spec.rb +60 -0
  108. data/spec/spec_helper.rb +47 -0
  109. data/spec/support/shared_examples/controller_examples.rb +45 -0
  110. data/spec/support/votes_controller_base_class.rb +28 -0
  111. data/spec/support/votes_controller_helpers.rb +24 -0
  112. data/spec/voting_builder/builder_spec.rb +66 -0
  113. data/spec/voting_builder/elements/link_spec.rb +31 -0
  114. data/spec/voting_builder/elements/span_spec.rb +44 -0
  115. data/spec/voting_builder/router_spec.rb +57 -0
  116. data/spec/voting_builder/structure_spec.rb +59 -0
  117. 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'
@@ -0,0 +1,6 @@
1
+ en:
2
+ errors:
3
+ duplication: "You have already selected this option!"
4
+ cannot_change: "You are not permitted to change the vote!"
5
+ require_login : "You must be logged in to vote!"
6
+
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Neutral::Engine.routes.draw do
2
+ resources :votes, only: [:create, :update, :destroy]
3
+ end
@@ -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,5 @@
1
+ en:
2
+ errors:
3
+ duplication: "You have already selected this option!"
4
+ cannot_change: "You are not permitted to change the vote!"
5
+ require_login : "You must be logged in to vote!"
@@ -0,0 +1,12 @@
1
+ class CreateNeutralVotes < ActiveRecord::Migration
2
+ def change
3
+ create_table :neutral_votes do |t|
4
+ t.string :voteable_type
5
+ t.integer :voteable_id
6
+ t.integer :voter_id
7
+ t.integer :value
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ 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,9 @@
1
+ class DropNeutralVotesTable < ActiveRecord::Migration
2
+ def up
3
+ drop_table :neutral_votes
4
+ end
5
+
6
+ def down
7
+ raise ActiveRecord::IrreversibleMigration
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class DropNeutralVotingsTable < ActiveRecord::Migration
2
+ def up
3
+ drop_table :neutral_votings
4
+ end
5
+
6
+ def down
7
+ raise ActiveRecord::IrreversibleMigration
8
+ end
9
+ 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,9 @@
1
+ module Neutral
2
+ module Helpers
3
+ module Routes
4
+ def neutral
5
+ mount Neutral::Engine => '/neutral', as: 'neutral'
6
+ end
7
+ end
8
+ end
9
+ 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,3 @@
1
+ module Neutral
2
+ VERSION = "0.0.1"
3
+ 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