e9_polls 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/README.rdoc +30 -0
  4. data/Rakefile +2 -0
  5. data/app/controllers/e9_polls/polls_controller.rb +125 -0
  6. data/app/helpers/e9_polls/polls_helper.rb +108 -0
  7. data/app/models/poll.rb +49 -0
  8. data/app/models/poll_answer.rb +20 -0
  9. data/app/views/e9_polls/poll_answers/_nested_attribute_template.html.haml +9 -0
  10. data/app/views/e9_polls/poll_answers/_poll_answer.html.haml +18 -0
  11. data/app/views/e9_polls/polls/_form.html.haml +26 -0
  12. data/app/views/e9_polls/polls/_poll.html.haml +15 -0
  13. data/app/views/e9_polls/polls/_results.html.haml +6 -0
  14. data/app/views/e9_polls/polls/_results_inner.html.haml +2 -0
  15. data/app/views/e9_polls/polls/_table.html.haml +18 -0
  16. data/app/views/e9_polls/polls/create.js.erb +6 -0
  17. data/app/views/e9_polls/polls/destroy.js.erb +3 -0
  18. data/app/views/e9_polls/polls/edit.html.haml +2 -0
  19. data/app/views/e9_polls/polls/index.html.haml +10 -0
  20. data/app/views/e9_polls/polls/index.js.erb +1 -0
  21. data/app/views/e9_polls/polls/new.html.haml +2 -0
  22. data/app/views/e9_polls/polls/results.html.haml +2 -0
  23. data/app/views/e9_polls/polls/show.html.haml +2 -0
  24. data/app/views/e9_polls/polls/update.js.erb +8 -0
  25. data/config/locales/en.yml +38 -0
  26. data/config/routes.rb +17 -0
  27. data/e9_polls.gemspec +26 -0
  28. data/e9_polls.js +44 -0
  29. data/lib/e9_polls/global_helper.rb +26 -0
  30. data/lib/e9_polls/model.rb +14 -0
  31. data/lib/e9_polls/version.rb +3 -0
  32. data/lib/e9_polls.rb +26 -0
  33. data/lib/generators/e9_polls/install_generator.rb +32 -0
  34. data/lib/generators/e9_polls/templates/initializer.rb +11 -0
  35. data/lib/generators/e9_polls/templates/javascript.js +115 -0
  36. data/lib/generators/e9_polls/templates/migration.rb +13 -0
  37. data/lib/generators/e9_polls/templates/stylesheet.css +24 -0
  38. metadata +124 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in e9_polls.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,30 @@
1
+ ** NOTE - This gem depends on e9_base, but does not reference it. It WILL NOT FUNCTION for apps which aren't built on the e9 Rails 3 CMS **
2
+
3
+ == E9Polls
4
+
5
+ Provites a Poll renderable for the e9 Rails 3 CMS.
6
+
7
+ == Installation
8
+
9
+ 1. Include the gem and run the install generator to copy over the necessary files,
10
+ then migrate.
11
+
12
+ rails g e9_polls:install
13
+
14
+ This will install the db migration, the JS and CSS required for the plugin to
15
+ function properly, and an initializer.
16
+
17
+ Modify the CSS as you see fit and the JS as required (carefully).
18
+
19
+ Check out the initializer and modify if necessary. For non-Ajax fallbacks it uses
20
+ the 'application' layout. This should be changed if the app doesn't use application
21
+ layout as a sensible default.
22
+
23
+ 2. Migrate the database.
24
+
25
+ rake db:migrate
26
+
27
+ 3. Finally, include the generated javascript and css (e9_polls.js and e9_polls.css)
28
+ in the fashion suited to the app.
29
+
30
+ 4. There is no #4.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,125 @@
1
+ class E9Polls::PollsController < AdminController
2
+ include E9Rails::Helpers::Translation
3
+ include E9Rails::Helpers::Title
4
+ include E9Rails::Helpers::ResourceErrorMessages
5
+ include E9Rails::Helpers::Pagination
6
+ include E9Rails::Controllers::Orderable
7
+
8
+ inherit_resources
9
+
10
+ respond_to :json, :only => [:answer, :show]
11
+
12
+ defaults :route_prefix => :admin, :resource_class => Poll
13
+ before_filter :except => [:show, :answer, :results] {|c| @route_scope = :admin }
14
+ skip_before_filter :authenticate_user!, :filter_access_filter, :only => [:show, :answer, :results]
15
+
16
+ add_resource_breadcrumbs
17
+
18
+ def create
19
+ create! { collection_path }
20
+ end
21
+
22
+ def update
23
+ update! do |format|
24
+ format.html { redirect_to collection_path }
25
+ format.js { resource.reload; render }
26
+ end
27
+ end
28
+
29
+ def answer
30
+ object = resource
31
+
32
+ if vote = params[resource_instance_name] && params[resource_instance_name][:vote]
33
+ if cookie[object.id].present?
34
+ # already voted error
35
+ object.errors.add(:vote, :already_voted)
36
+ else
37
+ # new vote OK!
38
+ object.vote = cookie[object.id] = vote
39
+ store_cookie
40
+ end
41
+ else
42
+ object.errors.add(:vote, :no_argument)
43
+ end
44
+
45
+ respond_with(object) do |format|
46
+ format.html { redirect_to poll_url(object) }
47
+
48
+ format.json do
49
+ if object.errors[:vote].present?
50
+ flash[:alert] = object.errors[:vote]
51
+ head 400
52
+ else
53
+ flash[:notice] = I18n.t(:success_message, :scope => :e9_polls)
54
+ render(:json => { :poll => object, :html => render_html_for_action('results') })
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def results
61
+ show! do |format|
62
+ format.json do
63
+ render(:json => { :poll => resource, :html => render_html_for_action })
64
+ end
65
+ end
66
+ end
67
+
68
+ def show
69
+ show! do |format|
70
+ format.json do
71
+ render(:json => { :poll => resource, :html => render_html_for_action })
72
+ end
73
+ end
74
+ end
75
+
76
+ protected
77
+
78
+ def render_html_for_action(action = nil)
79
+ action ||= params[:action]
80
+
81
+ html = nil
82
+
83
+ lookup_context.update_details(:formats => [Mime::HTML.to_sym]) do
84
+ html = render_to_string(action, :layout => false)
85
+ end
86
+
87
+ html
88
+ end
89
+
90
+ def cookie
91
+ @_e9_polls_cookie ||= Marshal.load(cookies[E9Polls.cookie_name]) rescue {}
92
+ end
93
+
94
+ def store_cookie
95
+ cookies.permanent[E9Polls.cookie_name] = {
96
+ :value => Marshal.dump(cookie),
97
+ :expires => 1.year.from_now
98
+ }
99
+ end
100
+
101
+ def parent; end
102
+ helper_method :parent
103
+
104
+ def collection
105
+ get_collection_ivar || set_collection_ivar(end_of_association_chain.paginate(pagination_parameters))
106
+ end
107
+
108
+ def default_ordered_on
109
+ 'name'
110
+ end
111
+
112
+ def default_ordered_dir
113
+ 'ASC'
114
+ end
115
+
116
+ def determine_layout
117
+ if request.xhr?
118
+ false
119
+ elsif %w(show results answer).member? params[:action]
120
+ E9Polls.fallback_html_layout
121
+ else
122
+ super
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,108 @@
1
+ module E9Polls::PollsHelper
2
+
3
+ ##
4
+ # Util
5
+ #
6
+
7
+ def html_concat(*chunks)
8
+ ''.html_safe.tap do |html|
9
+ chunks.each {|chunk| html.safe_concat("#{chunk}\n") }
10
+ end
11
+ end
12
+
13
+ def e9_polls_resource_link(action, record, opts = {})
14
+ opts.symbolize_keys!
15
+
16
+ # NOTE cascading backend for i18n would accomplish the lookup without defaults
17
+
18
+ action = action.to_sym
19
+ klass = record.is_a?(Class) ? record : record.class
20
+ scope = "e9_polls.#{klass.model_name.collection}"
21
+
22
+ # NOTE this assumes the definition of #parent, which is added by IR but only on controllers
23
+ # with polymorphic belongs_to relationships (by default).
24
+ scopes = [*(opts[:scope] || @route_scope), parent].compact
25
+ path = case action.to_sym
26
+ when :new; new_polymorphic_path(scopes << klass)
27
+ when :edit; edit_polymorphic_path(scopes << record)
28
+ else polymorphic_path(scopes << record)
29
+ end
30
+
31
+ if action == :destroy
32
+ opts[:method] = :delete
33
+ opts.reverse_merge!({
34
+ :confirm => t(:"#{scope}.confirm_destroy",
35
+ :default => :"e9_polls.confirm_destroy")
36
+ })
37
+ end
38
+
39
+ link_to t(:"#{scope}.#{action}", :default => :"e9_polls.#{action}"), path, opts
40
+ end
41
+
42
+ ##
43
+ # Links
44
+ #
45
+
46
+ def records_table_links_for_record(record)
47
+ if respond_to?(method_name = "records_table_links_for_#{record.class.name.underscore}")
48
+ send(method_name, record)
49
+ else
50
+ html_concat(
51
+ )
52
+ end
53
+ end
54
+
55
+ def link_to_add_nested_attribute(association_name)
56
+ link_to( t(:add_nested_attribute, :scope => :e9_polls), '#', :class => 'add-nested-association', 'data-association' => association_name)
57
+ end
58
+
59
+ def link_to_destroy_nested_attribute
60
+ link_to( t(:destroy_nested_attribute, :scope => :e9_polls), '#', :class => 'destroy-nested-association')
61
+ end
62
+
63
+ def render_nested_attribute_association(association_name, form, options = {})
64
+ options.symbolize_keys!
65
+
66
+ association = resource.send(association_name)
67
+
68
+ unless association.empty?
69
+ form.fields_for(association_name) do |f|
70
+ concat nested_attribute_template(association_name, f, options)
71
+ end
72
+ end
73
+ end
74
+
75
+ def nested_attribute_template_js(klass, association_name, options = {})
76
+ options.symbolize_keys!
77
+ options[:index] ||= 10000
78
+
79
+ template = nil
80
+
81
+ fields_for(klass.new) do |f|
82
+ f.object.send(association_name).build
83
+ f.fields_for(association_name, :child_index => options[:index]) do |ff|
84
+ template = "#{escape_javascript(nested_attribute_template(association_name, ff))}"
85
+ end
86
+ end
87
+
88
+ retv = <<-RETV
89
+ TEMPLATES = window.TEMPLATES || {};
90
+ TEMPLATES['#{association_name}'] = {
91
+ index: #{options[:index]},
92
+ template: "#{template}"
93
+ };
94
+ RETV
95
+ end
96
+
97
+ def nested_attribute_template(association_name, builder, options = {})
98
+ options.symbolize_keys!
99
+ partial = options[:partial] || File.join('e9_polls', builder.object.class.model_name.collection, 'nested_attribute_template')
100
+ render(:partial => partial, :locals => { :f => builder })
101
+ end
102
+
103
+ def build_associated_resource(association_name)
104
+ params_method = "#{association_name}_build_parameters"
105
+ build_params = resource_class.send(params_method) if resource_class.respond_to?(params_method)
106
+ resource.send(association_name).build(build_params || {})
107
+ end
108
+ end
@@ -0,0 +1,49 @@
1
+ class Poll < ::Renderable
2
+ include E9Polls::Model
3
+
4
+ has_many :poll_answers
5
+
6
+ accepts_nested_attributes_for :poll_answers, :allow_destroy => true, :reject_if => :reject_answer?
7
+
8
+ validates :template, :presence => true
9
+
10
+ def vote
11
+ nil
12
+ end
13
+
14
+ def as_json(options={})
15
+ {}.tap do |hash|
16
+ hash[:id] = self.id,
17
+ hash[:name] = self.name,
18
+ hash[:question] = self.question,
19
+ hash[:answers] = self.answers,
20
+ hash[:errors] = self.errors
21
+ end
22
+ end
23
+
24
+ def question
25
+ template
26
+ end
27
+
28
+ def answers
29
+ poll_answers
30
+ end
31
+
32
+ def vote=(id)
33
+ poll_answers.find_by_id(id).try(:vote!)
34
+ end
35
+
36
+ def votes
37
+ poll_answers.sum(:votes)
38
+ end
39
+
40
+ def percentage_for(poll_answer)
41
+ votes.zero? ? 0 : (poll_answer.votes.to_f / votes * 100).round(2)
42
+ end
43
+
44
+ protected
45
+
46
+ def reject_answer?(attributes)
47
+ attributes.keys.member?(:value) && attributes[:value].blank?
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ class PollAnswer < ActiveRecord::Base
2
+ include E9Polls::Model
3
+ belongs_to :poll
4
+
5
+ def as_json(options={})
6
+ {}.tap do |hash|
7
+ hash[:value] = self.value
8
+ hash[:votes] = self.votes
9
+ hash[:percentage] = self.percentage
10
+ end
11
+ end
12
+
13
+ def vote!
14
+ increment!(:votes)
15
+ end
16
+
17
+ def percentage
18
+ poll.percentage_for(self)
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ .poll-answer.nested-association
2
+ - if f.object.persisted?
3
+ = f.hidden_field :id
4
+ = f.hidden_field :_destroy, :value => 0
5
+
6
+ .field
7
+ = f.text_area :value
8
+
9
+ = link_to_destroy_nested_attribute
@@ -0,0 +1,18 @@
1
+ .poll-answer{:id => poll_answer.to_anchor}
2
+ - if f = local_assigns[:f]
3
+ - unless poll_answered?(poll_answer.poll)
4
+ = f.radio_button :vote, poll_answer.id, :id => "#{poll_answer.to_anchor}_input"
5
+ %label{:for => "#{poll_answer.to_anchor}_input"}
6
+ = poll_answer.value
7
+ - if poll_answered?(poll_answer.poll, poll_answer)
8
+ %span.poll-answer-voted
9
+ = t(:your_vote, :scope => :e9_polls)
10
+ - else
11
+ %span.poll-answer-value
12
+ = poll_answer.value
13
+ %span.poll-answer-bar-outer
14
+ %span.poll-answer-bar{:style => "width:#{poll_answer.percentage}%", :class => "pab-#{cycle(*(1..(E9Polls.bar_css_class_count)).to_a)}"}
15
+ %span.poll-answer-votes
16
+ = poll_answer.votes
17
+ %span.poll-answer-percentage
18
+ #{poll_answer.percentage}%
@@ -0,0 +1,26 @@
1
+ = form_for resource, :url => polymorphic_path([:admin, parent, resource]), :remote => request.xhr? do |f|
2
+ .errors= resource_error_messages!
3
+
4
+ .field
5
+ = f.label :name, nil, :class => :req
6
+ = f.text_field :name
7
+
8
+ .field
9
+ = f.label :template, nil, :class => :req
10
+ = f.text_field :template
11
+
12
+ %fieldset.poll-answers.nested-associations
13
+ %legend
14
+ %span= f.label(:poll_answers)
15
+ = link_to_add_nested_attribute(:poll_answers)
16
+ = render_nested_attribute_association(:poll_answers, f)
17
+
18
+ - unless request.xhr?
19
+ = render 'shared/admin/region_type_select', :f => f
20
+
21
+ .actions
22
+ = f.submit
23
+
24
+ -# the js templates for adding fields, it would probably be nice if this was rendered once, perhaps
25
+ :javascript
26
+ #{nested_attribute_template_js(Poll, :poll_answers)}
@@ -0,0 +1,15 @@
1
+ .e9-poll
2
+ .poll-question
3
+ = poll.question
4
+
5
+ .poll-answers
6
+ - if poll_answered?(poll)
7
+ = render(:partial => 'e9_polls/polls/results_inner', :locals => { :poll => poll })
8
+
9
+ - else
10
+ = form_for poll, :url => answer_poll_path(poll) do |f|
11
+ = render(:partial => PollAnswer.model_name.partial_path, :collection => poll.poll_answers, :locals => { :f => f })
12
+ .actions
13
+ = f.submit t(:submit_answer, :scope => :e9_polls)
14
+
15
+ = poll_results_link(poll)
@@ -0,0 +1,6 @@
1
+ .e9-poll
2
+ .poll-question
3
+ = poll.question
4
+
5
+ .poll-answers
6
+ = render(:partial => 'e9_polls/polls/results_inner', :locals => { :poll => poll })
@@ -0,0 +1,2 @@
1
+ = render(:partial => PollAnswer.model_name.partial_path, :collection => poll.poll_answers)
2
+ = poll_form_link(poll) unless poll_answered?(poll)
@@ -0,0 +1,18 @@
1
+ %table.records
2
+ %thead
3
+ %tr
4
+ %th= resource_class.human_attribute_name(:name)
5
+ %th= e9_t(:actions)
6
+ %tbody
7
+ - if collection.empty?
8
+ %tr
9
+ %td{:colspan => 2}= e9_t(:no_records_text)
10
+ - else
11
+ - collection.each do |record|
12
+ %tr{:id => "ids_#{record.id}", :class => cycle('odd', 'even')}
13
+ %td.text
14
+ = record.name
15
+ %td.links
16
+ = poll_results_link(record)
17
+ = e9_polls_resource_link(:edit, record)
18
+ = e9_polls_resource_link(:destroy, record)
@@ -0,0 +1,6 @@
1
+ <% if resource.errors.any? %>
2
+ $('form .errors').html("<%= escape_javascript(resource_error_messages!) %>");
3
+ $.fn.colorbox.resize();
4
+ <% else %>
5
+ window.location.reload(true);
6
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% if !resource.errors.any? %>
2
+ $("#ids_<%= resource.id %>").fadeOut();
3
+ <% end %>
@@ -0,0 +1,2 @@
1
+ = title e9_t(:edit_title)
2
+ = render 'form'
@@ -0,0 +1,10 @@
1
+ = title (@index_title || e9_t(:index_title))
2
+
3
+ .toolbar
4
+ .toolbar-right
5
+ = e9_polls_resource_link(:new, resource_class)
6
+
7
+ %div#records_table
8
+ = render 'table', :resources => collection
9
+
10
+ = will_paginate collection
@@ -0,0 +1 @@
1
+ $("table.records").replaceWith("<%= escape_javascript(render('table', :collection => collection)) %>");
@@ -0,0 +1,2 @@
1
+ = title e9_t(:new_title)
2
+ = render 'form'
@@ -0,0 +1,2 @@
1
+ = title e9_t(:results_title) unless request.xhr?
2
+ = render('results', :poll => resource)
@@ -0,0 +1,2 @@
1
+ = title e9_t(:show_title) unless request.xhr?
2
+ = render(:partial => Poll.model_name.partial_path, :object => resource)
@@ -0,0 +1,8 @@
1
+ <% if resource.errors.any? %>
2
+ $('form .errors').html("<%= escape_javascript(resource_error_messages!) %>");
3
+ <% else %>
4
+ $('#poll_<%= resource.id %>').html("<%= escape_javascript(render(:partial => Poll.model_name.partial_path, :object => resource)) %>");
5
+ $('#poll_<%= resource.id %>').quick_edit(true);
6
+
7
+ try { $.colorbox.close() } catch(e) {}
8
+ <% end %>
@@ -0,0 +1,38 @@
1
+ en:
2
+ e9_polls:
3
+ destroy: Delete
4
+ edit: Edit
5
+ show: View
6
+ new: New
7
+ confirm_destroy: Are you sure? This cannot be undone.
8
+ add_nested_attribute: Add
9
+ destroy_nested_attribute: Remove
10
+ poll_results_link: View the results!
11
+ poll_form_link: Answer now!
12
+ poll_show_link_title: Submit your Answer
13
+ poll_results_link_title: Results of this Poll
14
+ already_voted: You've already voted.
15
+ submit_answer: Submit!
16
+ your_vote: (Your answer)
17
+ success_message: Thanks for participating in the poll!
18
+ polls:
19
+ new: Add a Poll
20
+ show: View Results
21
+
22
+ # compat with e9_t translation
23
+ e9:
24
+ e9_polls:
25
+ polls:
26
+ results_title: Poll Results
27
+ show_title: Viewing Poll
28
+
29
+
30
+ # AR record translations
31
+ activerecord:
32
+ errors:
33
+ models:
34
+ poll:
35
+ attributes:
36
+ vote:
37
+ already_voted: "Sorry but you've already voted on this poll!"
38
+ no_argument: "Oops, sorry but your vote was not able to be processed!"
data/config/routes.rb ADDED
@@ -0,0 +1,17 @@
1
+ Rails.application.routes.draw do
2
+ scope :module => :e9_polls do
3
+ scope :path => :admin, :as => :admin do
4
+ resources :polls, :except => :show, :controller => 'polls'
5
+ end
6
+
7
+ resources :polls, :only => :show, :controller => 'polls' do
8
+ member do
9
+ get :results
10
+ put :answer
11
+ end
12
+ end
13
+
14
+ # redirect admin show url to edit
15
+ get "/admin/polls/:id", :to => redirect("/admin/polls/%{id}/edit"), :constraints => { :id => /\d+/ }
16
+ end
17
+ end
data/e9_polls.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "e9_polls/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "e9_polls"
7
+ s.version = E9Polls::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Travis Cox"]
10
+ s.email = ["travis@e9digital.com"]
11
+ s.homepage = "http://github.com/e9digital/e9_polls"
12
+ s.summary = %q{Polls module for the e9 Rails 3 cms}
13
+ s.description = File.open('README.rdoc').read rescue nil
14
+
15
+ s.rubyforge_project = "e9_polls"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ # NOTE This gem depends on e9_base ~> 1.3, but cannot reference it
23
+ # because it is a private repository.
24
+ #
25
+ # s.add_dependency("e9_base", "~> 1.3.0")
26
+ end
data/e9_polls.js ADDED
@@ -0,0 +1,44 @@
1
+ ;jQuery(function($) {
2
+ var selector_prefix = 'body.controller-e9-polls-polls',
3
+ $selector = $(selector_prefix);
4
+
5
+ /**
6
+ * Adds a new nested assocation. Depends on the nested association
7
+ * js templates being loaded.
8
+ */
9
+ $('a.add-nested-association').click(function(e) {
10
+ e.preventDefault();
11
+
12
+ var $this = $(this),
13
+ $parent = $this.closest('.nested-associations'),
14
+ obj,
15
+ template,
16
+ index;
17
+
18
+ try {
19
+ obj = TEMPLATES[this.getAttribute('data-association')];
20
+ } catch(e) { return }
21
+
22
+ template = obj.template.replace(new RegExp(obj.index++, 'g'), obj.index);
23
+
24
+ $(template).appendTo($parent);
25
+ });
26
+
27
+ /**
28
+ * Effectively destroys an added nested association, removing the container
29
+ * the association is not persisted, or hiding it and setting the _destroy
30
+ * parameter for the association if it is.
31
+ */
32
+ $('a.destroy-nested-association').live('click', function(e) {
33
+ e.preventDefault();
34
+
35
+ var $parent = $(this).closest('.nested-association').hide(),
36
+ $destro = $parent.find('input[id$=__destroy]');
37
+
38
+ if ($destro.length) {
39
+ $destro.val('1');
40
+ } else {
41
+ $parent.remove();
42
+ }
43
+ });
44
+ });
@@ -0,0 +1,26 @@
1
+ module E9Polls
2
+ module GlobalHelper
3
+ def poll_answered?(poll, answer = nil)
4
+ if cookie = e9_polls_cookie
5
+ cookie[poll.id] && !answer || cookie[poll.id].to_s == answer.id.to_s
6
+ end
7
+ end
8
+
9
+ def poll_results_link(poll, options = {})
10
+ text = I18n.t(:poll_results_link, :scope => :e9_polls)
11
+ options.reverse_merge! :title => t(:poll_results_link_title, :scope => :e9_polls)
12
+ link_to text, options.delete(:url) || results_poll_path(poll), options.merge(:class => 'view-poll-results')
13
+ end
14
+
15
+ def poll_form_link(poll, options = {})
16
+ text = I18n.t(:poll_form_link, :scope => :e9_polls)
17
+ options.reverse_merge! :title => t(:poll_show_link_title, :scope => :e9_polls)
18
+ link_to text, options.delete(:url) || poll_path(poll), options.merge(:class => 'view-poll-form')
19
+ end
20
+
21
+ def e9_polls_cookie
22
+ return if @_e9_polls_cookie == false
23
+ @_e9_polls_cookie ||= Marshal.load(cookies[E9Polls.cookie_name]) rescue false
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ module E9Polls
2
+ #
3
+ # Very simple module to alter the model_name.partial_path of E9Polls models
4
+ #
5
+ module Model
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ unless self.model_name.partial_path =~ /^e9_polls/
10
+ self.model_name.instance_variable_set('@partial_path', File.join('e9_polls', self.model_name.partial_path).freeze)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module E9Polls
2
+ VERSION = "1.0.1"
3
+ end
data/lib/e9_polls.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'e9_base'
2
+
3
+ module E9Polls
4
+ autoload :VERSION, 'e9_polls/version'
5
+ autoload :Model, 'e9_polls/model'
6
+
7
+ mattr_accessor :fallback_html_layout
8
+ @@fallback_html_layout = 'application'
9
+
10
+ mattr_accessor :cookie_name
11
+ @@cookie_name = 'e9_polls'
12
+
13
+ mattr_accessor :bar_css_class_count
14
+ @@bar_css_class_count = 5
15
+
16
+ class Engine < ::Rails::Engine
17
+ config.e9_crm = E9Polls
18
+
19
+ initializer 'e9_polls.include_base_helper' do
20
+ ActiveSupport.on_load(:action_view) do
21
+ require 'e9_polls/global_helper'
22
+ include E9Polls::GlobalHelper
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module E9Polls
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ def self.source_root
10
+ File.join(File.dirname(__FILE__), 'templates')
11
+ end
12
+
13
+ def self.next_migration_number(dirname) #:nodoc:
14
+ if ActiveRecord::Base.timestamped_migrations
15
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
16
+ else
17
+ "%.3d" % (current_migration_number(dirname) + 1)
18
+ end
19
+ end
20
+
21
+ def create_migration
22
+ migration_template 'migration.rb', 'db/migrate/create_e9_polls.rb'
23
+ end
24
+
25
+ def copy_over_files
26
+ copy_file 'initializer.rb', 'config/initializers/e9_polls.rb'
27
+ copy_file 'javascript.js', 'public/javascripts/e9_polls.js'
28
+ copy_file 'stylesheet.css', 'public/stylesheets/e9_polls.css'
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ require 'e9_polls'
2
+
3
+ # the fallback HTML layout for polls views (typically JS renders)
4
+ E9Polls.fallback_html_layout = 'application'
5
+
6
+ # the name of the cookie which tracks client votes
7
+ E9Polls.cookie_name = 'e9_polls'
8
+
9
+ # the number of css classes for the "bars" in the results view that
10
+ # represent the answer count, defaults to 5, cycling (.pab-1, .pab-2, etc.)
11
+ E9Polls.bar_css_class_count = 5
@@ -0,0 +1,115 @@
1
+ ;jQuery(function($) {
2
+ $('body.admin table.records a.view-poll-results').colorbox({
3
+ transition: 'none',
4
+ width: '35%',
5
+ height: '50%',
6
+ onComplete: function() {
7
+ $('#cboxLoadedContent div.poll-question')
8
+ .replaceWith(function(i, content) {
9
+ return "<h1>" + content + "</h1>";
10
+ });
11
+ }
12
+ });
13
+
14
+ $('.renderable.poll form').live('submit', function(e) {
15
+ e.preventDefault();
16
+ var el = $(this);
17
+
18
+ // return if no selection
19
+ if (!$("input[@name='poll[vote]']:checked").val()) return;
20
+
21
+ $.ajax({
22
+ url: el.attr('action'),
23
+ data: el.serializeArray(),
24
+ dataType: 'json',
25
+ type: 'POST',
26
+ success: function(data, status, xhr) {
27
+ el.closest('.renderable.poll').html(data.html);
28
+ }
29
+ });
30
+ });
31
+
32
+ $('.poll .view-poll-results, .poll .view-poll-form').live('click', function(e) {
33
+ e.preventDefault();
34
+ var el = $(this);
35
+
36
+ $.ajax({
37
+ url: el.attr('href'),
38
+ dataType: 'json',
39
+ type: 'GET',
40
+ success: function(data, status, xhr) {
41
+ el.closest('.renderable.poll').html(data.html);
42
+ }
43
+ });
44
+ });
45
+
46
+ /*
47
+ * add poll class handler to quick_edit.
48
+ *
49
+ * TODO Come up with a nicer method of setting defaults
50
+ *
51
+ * NOTE It's probably overkill and unnecessary to worry about load order,
52
+ * which is the complicating factor here.
53
+ */
54
+ $.quick_edit = $.quick_edit || {};
55
+ $.quick_edit.class_handlers = $.extend({
56
+ 'poll' : function(el) {
57
+ var path = el.attr('data-renderable-path'),
58
+ npath = el.attr('data-update-node-path'),
59
+ //rpath = path + '/replace?node_id=' + el.attr('data-node'),
60
+ epath = path + '/edit';
61
+
62
+ return '<a class="qe-qelink" href="'+ epath +'">Edit</a>' +
63
+ '<a class="qe-ulink" href="'+ npath +'">Switch</a>' +
64
+ '<a class="qe-elink" href="'+ epath +'">Admin</a>';
65
+ }
66
+ }, $.quick_edit.class_handlers);
67
+
68
+
69
+ var selector_prefix = 'body.controller-e9-polls-polls',
70
+ $selector = $(selector_prefix);
71
+
72
+ /**
73
+ * Adds a new nested assocation. Depends on the nested association
74
+ * js templates being loaded.
75
+ */
76
+ $('a.add-nested-association').live('click', function(e) {
77
+ e.preventDefault();
78
+
79
+ var $this = $(this),
80
+ $parent = $this.closest('.nested-associations'),
81
+ obj,
82
+ template,
83
+ index;
84
+
85
+ try {
86
+ obj = TEMPLATES[this.getAttribute('data-association')];
87
+ } catch(e) { return }
88
+
89
+ template = obj.template.replace(new RegExp(obj.index++, 'g'), obj.index);
90
+
91
+ $(template).appendTo($parent);
92
+
93
+ try { $.colorbox.resize() } catch(e) {}
94
+ });
95
+
96
+ /**
97
+ * Effectively destroys an added nested association, removing the container
98
+ * the association is not persisted, or hiding it and setting the _destroy
99
+ * parameter for the association if it is.
100
+ */
101
+ $('a.destroy-nested-association').live('click', function(e) {
102
+ e.preventDefault();
103
+
104
+ var $parent = $(this).closest('.nested-association').hide(),
105
+ $destro = $parent.find('input[id$=__destroy]');
106
+
107
+ if ($destro.length) {
108
+ $destro.val('1');
109
+ } else {
110
+ $parent.remove();
111
+ }
112
+
113
+ try { $.colorbox.resize() } catch(e) {}
114
+ });
115
+ });
@@ -0,0 +1,13 @@
1
+ class CreateE9Polls < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :poll_answers, :force => true do |t|
4
+ t.references :poll
5
+ t.integer :votes, :default => 0
6
+ t.text :value
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ drop_table :poll_answers
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ .e9-poll { }
2
+ .poll-question { font-size: large; font-weight: bold; padding: 0 1.2em; }
3
+ .poll-answers { padding: 0 1.2em; margin-bottom: 1em; }
4
+ .poll-answer-bar { height: 15px; display: block; }
5
+ .poll-answer-bar-outer { display: block; background-color: #eee; }
6
+
7
+ .poll-answer { margin: 1em 0; clear: both; }
8
+ .poll-answer textarea { height: 3em; margin-bottom: 0; }
9
+ .poll-answer .field { margin-bottom: 0; }
10
+ form.edit-poll .actions { margin-top: 1em; }
11
+
12
+ /* Poll answers or poll percentage. */
13
+ .poll-answer-votes { display: none; }
14
+ .poll-answer-percentage {}
15
+
16
+ /*
17
+ * Individual bar styles
18
+ * (the default is 5 rotating classes, but this can be changed in config).
19
+ */
20
+ .pab-1 { background-color: Green; }
21
+ .pab-2 { background-color: Maroon; }
22
+ .pab-3 { background-color: LightSalmon; }
23
+ .pab-4 { background-color: LightSeaGreen; }
24
+ .pab-5 { background-color: HotPink; }
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: e9_polls
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Travis Cox
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-29 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: |
18
+ ** NOTE - This gem depends on e9_base, but does not reference it. It WILL NOT FUNCTION for apps which aren't built on the e9 Rails 3 CMS **
19
+
20
+ == E9Polls
21
+
22
+ Provites a Poll renderable for the e9 Rails 3 CMS.
23
+
24
+ == Installation
25
+
26
+ 1. Include the gem and run the install generator to copy over the necessary files,
27
+ then migrate.
28
+
29
+ rails g e9_polls:install
30
+
31
+ This will install the db migration, the JS and CSS required for the plugin to
32
+ function properly, and an initializer.
33
+
34
+ Modify the CSS as you see fit and the JS as required (carefully).
35
+
36
+ Check out the initializer and modify if necessary. For non-Ajax fallbacks it uses
37
+ the 'application' layout. This should be changed if the app doesn't use application
38
+ layout as a sensible default.
39
+
40
+ 2. Migrate the database.
41
+
42
+ rake db:migrate
43
+
44
+ 3. Finally, include the generated javascript and css (e9_polls.js and e9_polls.css)
45
+ in the fashion suited to the app.
46
+
47
+ 4. There is no #4.
48
+
49
+ email:
50
+ - travis@e9digital.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - README.rdoc
61
+ - Rakefile
62
+ - app/controllers/e9_polls/polls_controller.rb
63
+ - app/helpers/e9_polls/polls_helper.rb
64
+ - app/models/poll.rb
65
+ - app/models/poll_answer.rb
66
+ - app/views/e9_polls/poll_answers/_nested_attribute_template.html.haml
67
+ - app/views/e9_polls/poll_answers/_poll_answer.html.haml
68
+ - app/views/e9_polls/polls/_form.html.haml
69
+ - app/views/e9_polls/polls/_poll.html.haml
70
+ - app/views/e9_polls/polls/_results.html.haml
71
+ - app/views/e9_polls/polls/_results_inner.html.haml
72
+ - app/views/e9_polls/polls/_table.html.haml
73
+ - app/views/e9_polls/polls/create.js.erb
74
+ - app/views/e9_polls/polls/destroy.js.erb
75
+ - app/views/e9_polls/polls/edit.html.haml
76
+ - app/views/e9_polls/polls/index.html.haml
77
+ - app/views/e9_polls/polls/index.js.erb
78
+ - app/views/e9_polls/polls/new.html.haml
79
+ - app/views/e9_polls/polls/results.html.haml
80
+ - app/views/e9_polls/polls/show.html.haml
81
+ - app/views/e9_polls/polls/update.js.erb
82
+ - config/locales/en.yml
83
+ - config/routes.rb
84
+ - e9_polls.gemspec
85
+ - e9_polls.js
86
+ - lib/e9_polls.rb
87
+ - lib/e9_polls/global_helper.rb
88
+ - lib/e9_polls/model.rb
89
+ - lib/e9_polls/version.rb
90
+ - lib/generators/e9_polls/install_generator.rb
91
+ - lib/generators/e9_polls/templates/initializer.rb
92
+ - lib/generators/e9_polls/templates/javascript.js
93
+ - lib/generators/e9_polls/templates/migration.rb
94
+ - lib/generators/e9_polls/templates/stylesheet.css
95
+ has_rdoc: true
96
+ homepage: http://github.com/e9digital/e9_polls
97
+ licenses: []
98
+
99
+ post_install_message:
100
+ rdoc_options: []
101
+
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: "0"
116
+ requirements: []
117
+
118
+ rubyforge_project: e9_polls
119
+ rubygems_version: 1.6.2
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: Polls module for the e9 Rails 3 cms
123
+ test_files: []
124
+