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