acts_as_caesar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.gitignore +27 -0
  2. data/.rvmrc +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.textile +61 -0
  6. data/Rakefile +65 -0
  7. data/acts_as_caesar.gemspec +38 -0
  8. data/lib/acts_as_caesar/modules/act_as_candidate.rb +26 -0
  9. data/lib/acts_as_caesar/objects/candidate.rb +25 -0
  10. data/lib/acts_as_caesar/objects/key.rb +15 -0
  11. data/lib/acts_as_caesar/objects/notification.rb +24 -0
  12. data/lib/acts_as_caesar/objects/persistence.rb +45 -0
  13. data/lib/acts_as_caesar/objects/vote.rb +77 -0
  14. data/lib/acts_as_caesar/objects/voter.rb +43 -0
  15. data/lib/acts_as_caesar/rack.rb +63 -0
  16. data/lib/acts_as_caesar/rails/engine.rb +11 -0
  17. data/lib/acts_as_caesar/rails/view_helpers.rb +17 -0
  18. data/lib/acts_as_caesar/version.rb +3 -0
  19. data/lib/acts_as_caesar.rb +21 -0
  20. data/spec/assets/jasmine_helper.js.coffee +5 -0
  21. data/spec/assets/jquery_helper.js.coffee +2 -0
  22. data/spec/assets/vote_cache_spec.js.coffee +63 -0
  23. data/spec/assets/vote_persistence_spec.js.coffee +31 -0
  24. data/spec/assets/vote_spec.js.coffee +59 -0
  25. data/spec/dummy/README.rdoc +261 -0
  26. data/spec/dummy/Rakefile +7 -0
  27. data/spec/dummy/app/assets/javascripts/application.js +3 -0
  28. data/spec/dummy/app/assets/stylesheets/application.css +52 -0
  29. data/spec/dummy/app/controllers/albums_controller.rb +58 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  31. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  32. data/spec/dummy/app/mailers/.gitkeep +0 -0
  33. data/spec/dummy/app/models/.gitkeep +0 -0
  34. data/spec/dummy/app/models/album.rb +15 -0
  35. data/spec/dummy/app/views/albums/form.html.erb +10 -0
  36. data/spec/dummy/app/views/albums/index.html.erb +12 -0
  37. data/spec/dummy/app/views/layouts/application.html.erb +16 -0
  38. data/spec/dummy/config/application.rb +69 -0
  39. data/spec/dummy/config/boot.rb +10 -0
  40. data/spec/dummy/config/environment.rb +5 -0
  41. data/spec/dummy/config/environments/development.rb +30 -0
  42. data/spec/dummy/config/environments/production.rb +67 -0
  43. data/spec/dummy/config/environments/test.rb +34 -0
  44. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  45. data/spec/dummy/config/initializers/inflections.rb +15 -0
  46. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  47. data/spec/dummy/config/initializers/mongoid.rb +2 -0
  48. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  49. data/spec/dummy/config/initializers/session_store.rb +8 -0
  50. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  51. data/spec/dummy/config/locales/en.yml +5 -0
  52. data/spec/dummy/config/mongoid.yml +7 -0
  53. data/spec/dummy/config/routes.rb +7 -0
  54. data/spec/dummy/config.ru +4 -0
  55. data/spec/dummy/lib/assets/.gitkeep +0 -0
  56. data/spec/dummy/log/.gitkeep +0 -0
  57. data/spec/dummy/public/404.html +26 -0
  58. data/spec/dummy/public/422.html +26 -0
  59. data/spec/dummy/public/500.html +25 -0
  60. data/spec/dummy/public/favicon.ico +0 -0
  61. data/spec/dummy/script/rails +6 -0
  62. data/spec/dummy/spec/controllers/albums_controller_spec.rb +33 -0
  63. data/spec/dummy/spec/models/album_spec.rb +8 -0
  64. data/spec/javascripts/support/jasmine.yml +74 -0
  65. data/spec/javascripts/support/jasmine_config.rb +23 -0
  66. data/spec/javascripts/support/jasmine_runner.rb +32 -0
  67. data/spec/modules/act_as_candidate_spec.rb +43 -0
  68. data/spec/objects/candidate_spec.rb +37 -0
  69. data/spec/objects/notification_spec.rb +19 -0
  70. data/spec/objects/persistence_spec.rb +87 -0
  71. data/spec/objects/vote_spec.rb +131 -0
  72. data/spec/objects/voter_spec.rb +80 -0
  73. data/spec/rack_spec.rb +97 -0
  74. data/spec/rails/engine_spec.rb +11 -0
  75. data/spec/rails/view_helpers_spec.rb +30 -0
  76. data/spec/rails_helper.rb +8 -0
  77. data/spec/spec_helper.rb +4 -0
  78. data/vendor/assets/javascripts/acts_as_caesar.js +1 -0
  79. data/vendor/assets/javascripts/initialization.js.coffee +36 -0
  80. data/vendor/assets/javascripts/vote.js.coffee +45 -0
  81. data/vendor/assets/javascripts/vote_cache.js.coffee +42 -0
  82. data/vendor/assets/javascripts/vote_persistence.js.coffee +51 -0
  83. data/vendor/assets/javascripts/vote_view.js.coffee +23 -0
  84. metadata +329 -0
data/.gitignore ADDED
@@ -0,0 +1,27 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+
6
+ # testing
7
+ coverage
8
+ spec/javascripts/compiled
9
+ spec/javascripts/*.js
10
+
11
+ # dummy project
12
+ spec/dummy/log/*.log
13
+ spec/dummy/db/*.sqlite3
14
+ spec/dummy/.DS_Store
15
+ spec/dummy/*/.DS_Store
16
+ spec/dummy/*/*/.DS_Store
17
+ spec/dummy/*/*/*/.DS_Store
18
+ spec/dummy/*/*/*/*/.DS_Store
19
+ spec/dummy/test
20
+ spec/dummy/tmp
21
+
22
+ # .DS_Store files
23
+ .DS_Store
24
+ */.DS_Store
25
+ */*/.DS_Store
26
+ */*/*/.DS_Store
27
+ */*/*/*/.DS_Store
data/.rvmrc ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if ! rvm list | grep ruby-1.9.3 ; then
4
+ rvm install 1.9.3
5
+ fi
6
+
7
+ rvm 1.9.3
8
+
9
+ if ! rvm gemset list | grep platformq_acts_as_caesar ; then
10
+ rvm gemset create platformq_acts_as_caesar
11
+ fi
12
+
13
+ rvm 1.9.3@platformq_acts_as_caesar
14
+
15
+ if ! gem list | grep -q bundler ; then
16
+ gem install --no-ri --no-rdoc bundler
17
+ bundle install --without production
18
+ fi
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in acts_as_caesar.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright 2012 Platform Q Limited
2
+
3
+ MIT LICENSE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,61 @@
1
+ h1. Description
2
+
3
+ Acts As Caesar is a voting system - allowing up/down voting of objects.
4
+
5
+ h1. Installation
6
+
7
+ Simply add acts_as_caesar to your Gemfile
8
+
9
+ <pre><code>gem 'acts_as_caesar'</code></pre>
10
+
11
+ h2. Requirements
12
+
13
+ You must have Redis server installed. We will seek to make the persistence layer database agnostic over time.
14
+
15
+ You must have jQuery installed and JavaScript on the client-side is required.
16
+
17
+ h2. Configuration
18
+
19
+ Acts As Caesar will connect to Redis on localhost, port 6379 (default for Redis server), unless you override this by creating your own Redis connection using the code below (for Rails place this in @config/initializers/redis.rb@):
20
+
21
+ <pre><code>$redis = Redis.new(:host => 'localhost', :port => 6379)</code></pre>
22
+
23
+ Or for Heroku:
24
+
25
+ <pre><code>require 'uri'
26
+ uri = URI.parse(ENV["REDISTOGO_URL"])
27
+ $redis = Redis.new(host: uri.host, port: uri.port, password: uri.password)</code></pre>
28
+
29
+ h1. Usage
30
+
31
+ h2. In Rails projects
32
+
33
+ Add the JavaScript library to your app/assets/application.js file:
34
+
35
+ <pre><code>//= require acts_as_caesar</code></pre>
36
+
37
+ Mount the engine into your routes.rb file, under any path you wish:
38
+
39
+ <pre><code>mount ActsAsCaesar::Rails::Engine => "/acts_as_caesar"</code></pre>
40
+
41
+ In any ActiveModel-compatible you wish to be updated with their score include the ActAsCandidate module:
42
+
43
+ <pre><code>class Review < ActiveRecord::Base
44
+ include ActsAsCaesar::ActAsCandidate
45
+ end</code></pre>
46
+
47
+ In your views, where you wish to display the voting buttons, call the helper as so:
48
+
49
+ <pre><code><%= acts_as_caesar(candidate: review, voter: current_user, base_path: '/acts_as_caesar') %></code></pre>
50
+
51
+ *Note:* the above base_path should match the one used in the routes file.
52
+
53
+ h1. Testing
54
+
55
+ To run the Acts As Caesar tests, simply run @rake@, this will run both RSpec and Jasmine. To run just RSpec use @rake spec@ or to run just Jasmine use @rake jasmine_spec@
56
+
57
+ *Note:* the RSpec test suite expects MongoDB to be running on localhost:27017 without authentication. You can customise this in @spec/dummy/config/mongoid.yml@.
58
+
59
+ h1. License
60
+
61
+ Please see "LICENSE":https://github.com/PlatformQ/acts_as_caesar/blob/master/LICENSE
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+ require 'coffee-script'
4
+ require 'fileutils'
5
+
6
+ desc "Runs both Rspec and Jasmine specs"
7
+ task default: ['spec', 'jasmine_spec']
8
+
9
+ desc "Compiles CoffeeScript and runs Jasmine specs"
10
+ task :jasmine_spec do
11
+ Rake::Task['compile_coffeescript'].invoke
12
+ Rake::Task['jasmine:ci'].invoke
13
+ end
14
+
15
+ desc "Compiles CoffeeScript files"
16
+ task :compile_coffeescript do
17
+ puts "Compiling coffeescript"
18
+ [
19
+ {
20
+ input_dir: 'vendor/assets/javascripts/*',
21
+ output_dir: 'spec/javascripts/compiled'
22
+ },
23
+ {
24
+ input_dir: 'spec/assets/*',
25
+ output_dir: 'spec/javascripts'
26
+ }
27
+ ].each do |opts|
28
+ output_dir = File.join(File.dirname(__FILE__), opts[:output_dir])
29
+
30
+ Dir[File.join(output_dir, "*.js")].each { |f| FileUtils.rm f }
31
+ FileUtils.makedirs(output_dir)
32
+
33
+ Dir[File.join(File.dirname(__FILE__), opts[:input_dir])].each do |source_file|
34
+ next unless source_file =~ /#{opts[:input_dir]}\/(.*)\.coffee$/
35
+ print "."
36
+ output_file = File.join(output_dir, $1)
37
+
38
+ begin
39
+ FileUtils.makedirs(File.dirname(output_file))
40
+
41
+ File.open(output_file, 'w') do |f|
42
+ f.write CoffeeScript.compile File.read(source_file)
43
+ end
44
+ rescue Exception => e
45
+ puts "\nError in #{source_file}"
46
+ raise
47
+ end
48
+ end
49
+ end
50
+ puts ""
51
+ end
52
+
53
+ desc "Run all examples"
54
+ RSpec::Core::RakeTask.new(:spec) do |t|
55
+ t.rspec_opts = %w[--color]
56
+ end
57
+
58
+ begin
59
+ require 'jasmine'
60
+ load 'jasmine/tasks/jasmine.rake'
61
+ rescue LoadError
62
+ task :jasmine do
63
+ abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
64
+ end
65
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "acts_as_caesar/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "acts_as_caesar"
7
+ s.version = ActsAsCaesar::VERSION
8
+ s.authors = ["Ryan Townsend"]
9
+ s.email = ["ryan@ryantownsend.co.uk"]
10
+ s.homepage = "http://platformq.co.uk/"
11
+ s.summary = %q{Acts As Caesar is a voting system - allowing up/down voting of objects.}
12
+ s.description = %q{Acts As Caesar is a voting system - allowing up/down voting of objects.}
13
+
14
+ s.rubyforge_project = "acts_as_caesar"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'redis', '3.0.0.rc1'
22
+ s.add_dependency 'json', '~> 1.6.6'
23
+
24
+ # testing only
25
+ s.add_development_dependency 'rspec', '~> 2.8'
26
+ s.add_development_dependency 'rack', '~> 1.4'
27
+ s.add_development_dependency 'rack-test', '~> 0.6'
28
+ s.add_development_dependency 'simplecov', '~> 0.6'
29
+ s.add_development_dependency 'jasmine', '~> 1.1'
30
+ s.add_development_dependency 'coffee-script', '~> 2.2'
31
+
32
+ # integration testing only
33
+ s.add_development_dependency 'rspec-rails', '~> 2.9'
34
+ s.add_development_dependency 'rails', '~> 3.2'
35
+ s.add_development_dependency 'mongoid', '~> 2.4'
36
+ s.add_development_dependency 'bson_ext', '~> 1.5'
37
+ s.add_development_dependency 'jquery-rails', '~> 2.0'
38
+ end
@@ -0,0 +1,26 @@
1
+ require 'acts_as_caesar/objects/candidate'
2
+ require 'acts_as_caesar/objects/notification'
3
+
4
+ module ActsAsCaesar
5
+ module ActAsCandidate
6
+ def self.included(base)
7
+ Notification.subscribe(key: /^#{base.name.downcase}\_[\da-f]+$/) do |candidate|
8
+ candidate.key =~ /\_([\da-f]+)$/ &&
9
+ base.find($1).update_total_candidate_score
10
+ end
11
+ end
12
+
13
+ def as_candidate
14
+ @as_candidate = ActsAsCaesar::Candidate.new(self)
15
+ end
16
+
17
+ def update_total_candidate_score
18
+ self.score = total_candidate_score
19
+ save!
20
+ end
21
+
22
+ def total_candidate_score
23
+ @total_candidate_score ||= as_candidate.total
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'json'
2
+ require 'acts_as_caesar/objects/key'
3
+
4
+ module ActsAsCaesar
5
+ class Candidate
6
+ def initialize(source)
7
+ @source = source
8
+ end
9
+
10
+ def key
11
+ Key.for_source(source)
12
+ end
13
+
14
+ def to_json(*args)
15
+ key
16
+ end
17
+
18
+ def total
19
+ @total ||= Persistence.vote_total(self)
20
+ end
21
+
22
+ private
23
+ attr_reader :source
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ module ActsAsCaesar
2
+ class Key
3
+ class << self
4
+ def for_source(source)
5
+ if source.respond_to?(:acts_as_caesar_key)
6
+ source.acts_as_caesar_key
7
+ elsif source.respond_to?(:id)
8
+ "#{source.class.name.downcase}_#{source.id}"
9
+ else
10
+ source.to_s
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module ActsAsCaesar
2
+ class Notification
3
+ class << self
4
+ # publishes a change to a candidate
5
+ def publish(object)
6
+ subscriptions.each do |subscription|
7
+ if object.key =~ subscription[:options][:key]
8
+ subscription[:process].yield(object)
9
+ end
10
+ end
11
+ end
12
+
13
+ # subscribes to changes on a candidate
14
+ def subscribe(options = {}, &block)
15
+ @subscriptions ||= []
16
+ @subscriptions << { options: options, process: block }
17
+ end
18
+
19
+ def subscriptions
20
+ @subscriptions || []
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,45 @@
1
+ require 'redis'
2
+ require 'acts_as_caesar/objects/notification'
3
+
4
+ module ActsAsCaesar
5
+ class Persistence
6
+ class << self
7
+ def connection
8
+ $redis ||= Redis.new(:host => 'localhost', :port => 6379)
9
+ end
10
+
11
+ def increment_total_votes_on(object)
12
+ result = connection.incr("#{object.key}_total")
13
+ Notification.publish(object)
14
+ result
15
+ end
16
+
17
+ def decrement_total_votes_on(object)
18
+ result = connection.decr("#{object.key}_total")
19
+ Notification.publish(object)
20
+ result
21
+ end
22
+
23
+ def vote_total(object)
24
+ (connection.get("#{object.key}_total") || 0).to_i
25
+ end
26
+
27
+ def vote_value(options)
28
+ key = [options[:candidate], options[:voter]].map(&:key).join("_")
29
+ (connection.get(key) || 0).to_i
30
+ end
31
+
32
+ def set_vote_value(value, options = {})
33
+ # load the keys
34
+ key = [options[:candidate], options[:voter]].map(&:key).join("_")
35
+ # if the value was zero, delete it
36
+ if value == 0
37
+ connection.del(key)
38
+ # if the value isn't zero, update it
39
+ else
40
+ connection.set(key, value)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,77 @@
1
+ require 'acts_as_caesar/objects/persistence'
2
+
3
+ module ActsAsCaesar
4
+ class Vote
5
+ class << self
6
+ def find(options)
7
+ from_value(Persistence.vote_value(options), options)
8
+ end
9
+
10
+ def from_value(value, options = {})
11
+ case value
12
+ when 1
13
+ PositiveVote
14
+ when -1
15
+ NegativeVote
16
+ else
17
+ NoVote
18
+ end.new(options)
19
+ end
20
+
21
+ def value
22
+ 0
23
+ end
24
+ end
25
+
26
+ def voter
27
+ @attrs[:voter]
28
+ end
29
+
30
+ def candidate
31
+ @attrs[:candidate]
32
+ end
33
+
34
+ def initialize(attrs)
35
+ @attrs = attrs
36
+ end
37
+
38
+ def value
39
+ self.class.value
40
+ end
41
+
42
+ def update(new_value)
43
+ raise ArgumentError, "Only accepts Integer objects" unless new_value.kind_of?(Integer)
44
+ # don't change if the values already match
45
+ return self if new_value == value
46
+ # set the new value
47
+ Persistence.set_vote_value(new_value, @attrs)
48
+ # if the value has increased
49
+ if new_value > value
50
+ Persistence.increment_total_votes_on(candidate)
51
+ # if the value has decreased
52
+ elsif new_value < value
53
+ Persistence.decrement_total_votes_on(candidate)
54
+ end
55
+ # return the new value object
56
+ self.class.from_value(new_value, @attrs)
57
+ end
58
+
59
+ def to_json(*args)
60
+ {
61
+ voter: voter.to_json,
62
+ candidate: candidate.to_json,
63
+ value: value
64
+ }
65
+ end
66
+ end
67
+
68
+ class NoVote < Vote; end
69
+
70
+ class PositiveVote < Vote
71
+ def self.value; 1; end
72
+ end
73
+
74
+ class NegativeVote < Vote
75
+ def self.value; -1; end
76
+ end
77
+ end
@@ -0,0 +1,43 @@
1
+ require 'acts_as_caesar/objects/vote'
2
+ require 'acts_as_caesar/objects/key'
3
+
4
+ module ActsAsCaesar
5
+ class Voter
6
+ def initialize(source)
7
+ @source = source || NoVoter.new
8
+ end
9
+
10
+ def has_voted_on?(candidate)
11
+ !vote_placed_on(candidate).kind_of?(NoVote)
12
+ end
13
+
14
+ def vote_value_for(candidate)
15
+ vote_placed_on(candidate).value
16
+ end
17
+
18
+ def update_vote(candidate, value)
19
+ vote_placed_on(candidate).update(value)
20
+ end
21
+
22
+ def vote_placed_on(candidate)
23
+ Vote.find(voter: self, candidate: candidate)
24
+ end
25
+
26
+ def key
27
+ Key.for_source(source)
28
+ end
29
+
30
+ def to_json(*args)
31
+ key
32
+ end
33
+
34
+ private
35
+ attr_reader :source
36
+ end
37
+
38
+ class NoVoter
39
+ def acts_as_caesar_key
40
+ ''
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,63 @@
1
+ require 'acts_as_caesar/objects/voter'
2
+ require 'acts_as_caesar/objects/candidate'
3
+ require 'json'
4
+ require 'rack/request'
5
+
6
+ module Rack
7
+ class ActsAsCaesar
8
+ attr_reader :request, :app
9
+
10
+ def initialize(app = nil)
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ # pass through app if response already exists
16
+ if app
17
+ status, headers, body = app.call(env)
18
+ return [status, headers, body] unless status == 404
19
+ end
20
+
21
+ @request = Rack::Request.new(env)
22
+ if request.path =~ /\/votes\/(\w+)\/([\w\-]+).json/
23
+ if request.get?
24
+ render_votes($1, $2)
25
+ elsif request.post?
26
+ update_vote($1, $2, request.params['value'].to_i)
27
+ else
28
+ method_not_allowed
29
+ end
30
+ else
31
+ not_found
32
+ end
33
+ end
34
+
35
+ private
36
+ def render_votes(voter, candidates)
37
+ voter = ::ActsAsCaesar::Voter.new(voter)
38
+ votes = candidates.split('-').map do |c|
39
+ ::ActsAsCaesar::Candidate.new(c)
40
+ end.map do |c|
41
+ voter.vote_placed_on(c)
42
+ end
43
+ # return an array of vote objects
44
+ [200, {"Content-Type" => "application/json"}, [JSON.generate(votes.map(&:to_json))]]
45
+ end
46
+
47
+ def update_vote(voter, candidate, value)
48
+ voter = ::ActsAsCaesar::Voter.new(voter)
49
+ candidate = ::ActsAsCaesar::Candidate.new(candidate)
50
+ vote = voter.update_vote(candidate, value)
51
+ # return the vote object
52
+ [200, {"Content-Type" => "application/json"}, [JSON.generate(vote.to_json)]]
53
+ end
54
+
55
+ def not_found
56
+ [404, {}, ["ActsAsCaesar - Action not found"]]
57
+ end
58
+
59
+ def method_not_allowed
60
+ [405, {}, ["ActsAsCaesar - Method not allowed"]]
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,11 @@
1
+ require 'acts_as_caesar/rack'
2
+
3
+ module ActsAsCaesar
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ initializer "engine.add_middleware" do |app|
7
+ app.middleware.use Rack::ActsAsCaesar
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require 'acts_as_caesar/objects/candidate'
2
+ require 'acts_as_caesar/objects/voter'
3
+
4
+ module ActsAsCaesar
5
+ module Rails
6
+ module ViewHelpers
7
+ def acts_as_caesar(options = {})
8
+ content_tag(:div, options[:content] || '', (options[:html] || {}).merge({
9
+ "data-candidate" => Candidate.new(options[:candidate]).key,
10
+ "data-voter" => Voter.new(options[:voter]).key,
11
+ "data-acts-as-caesar" => 'uninit',
12
+ "data-base-path" => options[:base_path]
13
+ }.delete_if { |k,v| v.blank? }))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module ActsAsCaesar
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,21 @@
1
+ require "acts_as_caesar/version"
2
+ require "acts_as_caesar/objects/persistence"
3
+ require "acts_as_caesar/objects/candidate"
4
+ require "acts_as_caesar/objects/voter"
5
+ require "acts_as_caesar/modules/act_as_candidate"
6
+
7
+ if defined?(::Rails)
8
+ require "acts_as_caesar/rails/engine"
9
+ if defined?(::ActiveSupport)
10
+ require "acts_as_caesar/rails/view_helpers"
11
+ ::ActiveSupport.on_load(:action_view) do
12
+ include ActsAsCaesar::Rails::ViewHelpers
13
+ end
14
+ end
15
+ elsif defined?(Rack)
16
+ require "acts_as_caesar/rack"
17
+ end
18
+
19
+ module ActsAsCaesar
20
+ # Your code goes here...
21
+ end
@@ -0,0 +1,5 @@
1
+ beforeEach ->
2
+ this.addMatchers {
3
+ toBeInstanceOf: (expected) ->
4
+ !!(this.actual instanceof expected)
5
+ }
@@ -0,0 +1,2 @@
1
+ class window.jQuery
2
+ @ajax = (opts) ->