e9_polls 1.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 (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
+