radiant-polls-extension 1.0.0

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 (44) hide show
  1. data/CHANGELOG +27 -0
  2. data/HELP.textile +71 -0
  3. data/LICENSE +21 -0
  4. data/README.textile +112 -0
  5. data/Rakefile +129 -0
  6. data/VERSION +1 -0
  7. data/app/controllers/admin/polls_controller.rb +25 -0
  8. data/app/controllers/poll_response_controller.rb +35 -0
  9. data/app/models/option.rb +22 -0
  10. data/app/models/poll.rb +67 -0
  11. data/app/views/admin/polls/_form.html.haml +67 -0
  12. data/app/views/admin/polls/_option.html.haml +9 -0
  13. data/app/views/admin/polls/edit.html.haml +5 -0
  14. data/app/views/admin/polls/index.html.haml +72 -0
  15. data/app/views/admin/polls/new.html.haml +5 -0
  16. data/app/views/admin/polls/remove.html.haml +17 -0
  17. data/config/locales/en.yml +25 -0
  18. data/config/locales/en_available_tags.yml +187 -0
  19. data/config/routes.rb +6 -0
  20. data/db/migrate/001_create_polls.rb +12 -0
  21. data/db/migrate/002_create_options.rb +14 -0
  22. data/db/migrate/003_add_start_date_to_polls.rb +9 -0
  23. data/lib/poll_process.rb +23 -0
  24. data/lib/poll_tags.rb +408 -0
  25. data/lib/tasks/polls_extension_tasks.rake +56 -0
  26. data/polls_extension.rb +51 -0
  27. data/public/images/admin/new-poll.png +0 -0
  28. data/public/images/admin/poll.png +0 -0
  29. data/public/images/admin/recycle.png +0 -0
  30. data/public/images/admin/reset.png +0 -0
  31. data/public/javascripts/admin/date_selector.js +177 -0
  32. data/public/javascripts/admin/polls.js +47 -0
  33. data/public/javascripts/poll_check.js +52 -0
  34. data/public/stylesheets/admin/polls.css +93 -0
  35. data/public/stylesheets/polls.css +9 -0
  36. data/radiant-polls-extension.gemspec +83 -0
  37. data/spec/controllers/admin/polls_controller_spec.rb +16 -0
  38. data/spec/controllers/poll_response_controller_spec.rb +149 -0
  39. data/spec/integration/page_caching_spec.rb +76 -0
  40. data/spec/lib/poll_tags_spec.rb +415 -0
  41. data/spec/models/poll_spec.rb +50 -0
  42. data/spec/spec.opts +6 -0
  43. data/spec/spec_helper.rb +42 -0
  44. metadata +141 -0
@@ -0,0 +1,9 @@
1
+ .results {
2
+ font-size: .7em;
3
+ margin-left: 4em;
4
+ }
5
+
6
+ .results table td {
7
+ border-top: none;
8
+ padding: 0;
9
+ }
@@ -0,0 +1,83 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "radiant-polls-extension"
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Chase James", "David Cato", "Andrew vonderLuft"]
12
+ s.date = "2013-02-27"
13
+ s.description = "Enables polls on pages. Uses cookies instead of sessions"
14
+ s.email = "avonderluft@avlux.net"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ "CHANGELOG",
21
+ "HELP.textile",
22
+ "LICENSE",
23
+ "README.textile",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "app/controllers/admin/polls_controller.rb",
27
+ "app/controllers/poll_response_controller.rb",
28
+ "app/models/option.rb",
29
+ "app/models/poll.rb",
30
+ "app/views/admin/polls/_form.html.haml",
31
+ "app/views/admin/polls/_option.html.haml",
32
+ "app/views/admin/polls/edit.html.haml",
33
+ "app/views/admin/polls/index.html.haml",
34
+ "app/views/admin/polls/new.html.haml",
35
+ "app/views/admin/polls/remove.html.haml",
36
+ "config/locales/en.yml",
37
+ "config/locales/en_available_tags.yml",
38
+ "config/routes.rb",
39
+ "db/migrate/001_create_polls.rb",
40
+ "db/migrate/002_create_options.rb",
41
+ "db/migrate/003_add_start_date_to_polls.rb",
42
+ "lib/poll_process.rb",
43
+ "lib/poll_tags.rb",
44
+ "lib/tasks/polls_extension_tasks.rake",
45
+ "polls_extension.rb",
46
+ "public/images/admin/new-poll.png",
47
+ "public/images/admin/poll.png",
48
+ "public/images/admin/recycle.png",
49
+ "public/images/admin/reset.png",
50
+ "public/javascripts/admin/date_selector.js",
51
+ "public/javascripts/admin/polls.js",
52
+ "public/javascripts/poll_check.js",
53
+ "public/stylesheets/admin/polls.css",
54
+ "public/stylesheets/polls.css",
55
+ "spec/controllers/admin/polls_controller_spec.rb",
56
+ "spec/controllers/poll_response_controller_spec.rb",
57
+ "spec/integration/page_caching_spec.rb",
58
+ "spec/lib/poll_tags_spec.rb",
59
+ "spec/models/poll_spec.rb",
60
+ "spec/spec.opts",
61
+ "spec/spec_helper.rb"
62
+ ]
63
+ s.homepage = "https://github.com/avonderluft/radiant-polls-extension"
64
+ s.require_paths = ["lib"]
65
+ s.rubygems_version = "1.8.24"
66
+ s.summary = "Polls Extension for Radiant CMS"
67
+
68
+ if s.respond_to? :specification_version then
69
+ s.specification_version = 3
70
+
71
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
72
+ s.add_runtime_dependency(%q<radiant>, [">= 1.1.3"])
73
+ s.add_runtime_dependency(%q<radiant-cache_by_page-extension>, [">= 1.0.2"])
74
+ else
75
+ s.add_dependency(%q<radiant>, [">= 1.1.3"])
76
+ s.add_dependency(%q<radiant-cache_by_page-extension>, [">= 1.0.2"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<radiant>, [">= 1.1.3"])
80
+ s.add_dependency(%q<radiant-cache_by_page-extension>, [">= 1.0.2"])
81
+ end
82
+ end
83
+
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Admin::PollsController do
4
+ dataset :users
5
+
6
+ before(:each) do
7
+ login_as :existing
8
+ end
9
+
10
+ describe "index" do
11
+ it "should show the index template" do
12
+ get :index
13
+ response.should render_template('admin/polls/index')
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,149 @@
1
+ require 'spec_helper'
2
+
3
+ describe PollResponseController do
4
+
5
+ describe 'routing' do
6
+
7
+ it 'should route to the index action' do
8
+ params_from(:get, '/pages/1/poll_response').should == {
9
+ :controller => 'poll_response',
10
+ :action => 'index',
11
+ :page_id => '1'
12
+ }
13
+ end
14
+
15
+ it 'should route to the create action' do
16
+ params_from(:post, '/pages/1/poll_response').should == {
17
+ :controller => 'poll_response',
18
+ :action => 'create',
19
+ :page_id => '1'
20
+ }
21
+ end
22
+
23
+ end
24
+
25
+ describe :create do
26
+
27
+ before :each do
28
+ @page = mock_model(Page, :id => 1, :poll_cookies => [], :submitted_polls => [])
29
+ @poll = mock_model(Poll, :id => 42)
30
+ @cookies = mock('cookies')
31
+ @cookies.stub!(:[])
32
+ @cookies.stub!(:[]=)
33
+ controller.stub!(:cookies).and_return(@cookies)
34
+ @page.should_receive(:request=).with(an_instance_of(ActionController::TestRequest))
35
+ @page.should_receive(:response=).with(an_instance_of(ActionController::TestResponse))
36
+ @page.should_receive(:poll_cookies).with(@cookies).and_return([])
37
+ Page.should_receive(:find).with(duck_type(:to_i)).and_return(@page)
38
+ end
39
+
40
+ describe 'poll has not been previously submitted' do
41
+
42
+ before :each do
43
+ Poll.should_receive(:find).with(duck_type(:to_i)).and_return(@poll)
44
+ @page.should_receive(:submitted_polls=).with(@page.poll_cookies)
45
+ @page.should_receive(:url).once.and_return('/')
46
+ @cookies.should_receive(:[]=).with("poll_#{@poll.id.to_s}", an_instance_of(Hash))
47
+ @poll.should_receive(:submit_response).with(an_instance_of(Option))
48
+ @option = mock_model(Option)
49
+ Option.should_receive(:find).with(duck_type(:to_i)).and_return(@option)
50
+ @page.should have(0).submitted_polls
51
+ end
52
+
53
+ describe 'a current poll does not exist' do
54
+
55
+ before :each do
56
+ Poll.should_receive(:find_current).with(no_args()).and_return(nil)
57
+ end
58
+
59
+ it 'should not cache the response' do
60
+ post :create, :page_id => @page.id, :poll_id => @poll.id.to_s, :response_id => '111'
61
+ response.headers['Cache-Control'].should eql('no-cache')
62
+ end
63
+
64
+ it 'should instantiate the list of submitted polls with the poll ID' do
65
+ post :create, :page_id => @page.id, :poll_id => @poll.id.to_s, :response_id => '111'
66
+ @page.should have(1).submitted_polls
67
+ @page.submitted_polls.should eql([@poll.id])
68
+ end
69
+
70
+ it 'should add the poll to the list of submitted polls' do
71
+ @page.submitted_polls << @poll.id - 1
72
+ post :create, :page_id => @page.id, :poll_id => @poll.id.to_s, :response_id => '111'
73
+ @page.should have(2).submitted_polls
74
+ @page.submitted_polls.should eql([@poll.id - 1, @poll.id])
75
+ end
76
+
77
+ end
78
+
79
+ describe 'a current poll exists' do
80
+
81
+ before :each do
82
+ @current_poll = mock_model(Poll, :id => @poll.id)
83
+ Poll.should_receive(:find_current).with(no_args()).and_return(@current_poll)
84
+ end
85
+
86
+ it 'should instantiate the list of submitted polls with the poll ID' do
87
+ post :create, :page_id => @page.id, :poll_id => @poll.id.to_s, :response_id => '111'
88
+ @page.submitted_polls.should eql([@poll.id])
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ describe 'poll has been previously submitted' do
96
+
97
+ before :each do
98
+ Poll.should_receive(:find).with(duck_type(:to_i)).and_return(@poll)
99
+ @page.should_receive(:submitted_polls=).with(@page.poll_cookies)
100
+ @poll.should_not_receive(:submit_response)
101
+ @page.should_receive(:url).and_return('/')
102
+ Option.should_not_receive(:find)
103
+ @page.submitted_polls << @poll.id
104
+ @page.should have(1).submitted_poll
105
+ end
106
+
107
+ describe 'a current poll exists' do
108
+
109
+ it 'should not add the poll to the list of submitted polls' do
110
+ @current_poll = mock_model(Poll, :id => @poll.id)
111
+ Poll.should_receive(:find_current).with(no_args()).and_return(@current_poll)
112
+ post :create, :page_id => @page.id, :poll_id => @poll.id.to_s, :response_id => '111'
113
+ @page.should have(1).submitted_polls
114
+ @page.submitted_polls.should eql([@poll.id])
115
+ end
116
+
117
+ end
118
+
119
+ describe 'a current poll does not exist' do
120
+
121
+ it 'should not add the poll to the list of submitted polls' do
122
+ Poll.should_receive(:find_current).with(no_args()).and_return(nil)
123
+ post :create, :page_id => @page.id, :poll_id => @poll.id.to_s, :response_id => '111'
124
+ @page.submitted_polls.should eql([@poll.id])
125
+ end
126
+
127
+ end
128
+
129
+ end
130
+
131
+ describe 'poll is out of date' do
132
+
133
+ it 'should not alter a past poll' do
134
+ Poll.should_receive(:find).with(duck_type(:to_i)).and_return(@poll)
135
+ @page.should_receive(:submitted_polls=).with(@page.poll_cookies)
136
+ @page.should_receive(:url).and_return('/')
137
+ @poll.should_not_receive(:submit_response)
138
+ @current_poll = mock_model(Poll, :id => @poll.id + 1)
139
+ Poll.should_receive(:find_current).with(no_args()).and_return(@current_poll)
140
+ @current_poll.should_not_receive(:submit_response)
141
+ Option.should_not_receive(:find)
142
+ post :create, :page_id => @page.id, :poll_id => @poll.id.to_s, :response_id => '111'
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe Page, "with and without polls present" do
4
+
5
+ dataset :pages
6
+
7
+ before :all do
8
+ FileUtils.chdir RAILS_ROOT
9
+ @cache_dir = "#{RAILS_ROOT}/tmp/cache"
10
+ @cache_file = "#{@cache_dir}/meta/*/*"
11
+
12
+ end
13
+
14
+ before :each do
15
+ @page = pages(:home)
16
+ @default = 1.day
17
+ SiteController.page_cache_directory = @cache_dir
18
+ SiteController.perform_caching = true
19
+ SiteController.cache_timeout = @default
20
+ @expire_mins = @default.to_i/60
21
+ @cache = Radiant::Cache
22
+ @cache.clear
23
+ end
24
+
25
+ def page_is_cached(page)
26
+ if response.nil?
27
+ @cache.clear
28
+ false
29
+ else
30
+ ! response.headers['Cache-Control'].include?('no-cache')
31
+ end
32
+ end
33
+
34
+ def cache_expires
35
+ Time.now + `cat #{@cache_file}`.split('max-age=')[1].split(',')[0].to_i rescue nil
36
+ end
37
+
38
+ describe "- fetch of page without a poll" do
39
+ it "should render a page with default caching" do
40
+ get "#{@page.slug}"
41
+ response.should be_success
42
+ response.cache_timeout.should be_nil
43
+ page_is_cached(@page).should be_true
44
+ response.headers['Cache-Control'].should == "max-age=#{@default}, public"
45
+ end
46
+ end
47
+
48
+ ["single poll", "collection of polls"].each do |test|
49
+
50
+ describe "- fetch of page with a #{test}" do
51
+
52
+ before :each do
53
+ Poll.create(:id => 42, :title => "My Older Poll", :response_count => 0, :start_date => 2.days.ago)
54
+ Poll.create(:id => 43, :title => "My Newer Poll", :response_count => 0, :start_date => 1.day.ago)
55
+ poll_tag = "<r:poll><r:title/></r:poll>"
56
+ if test.eql?("collection of polls")
57
+ poll_tag = "<r:polls><r:each:poll><r:title/></r:each:poll></r:polls>"
58
+ end
59
+ @part = PagePart.find_by_page_id_and_name(@page, 'body')
60
+ @part.content += poll_tag
61
+ @part.save
62
+ get "#{@page.slug}"
63
+ @first_cached_page_expire_time = cache_expires
64
+ end
65
+
66
+ it "should render the page and cache it with defaults" do
67
+ response.should be_success
68
+ response.cache_timeout.should be_nil
69
+ cache_expires.should be_close(@expire_mins.minutes.from_now, 30)
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,415 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Poll Tags' do
4
+
5
+ dataset :pages
6
+
7
+ before do
8
+ options = { Option.new(:title => 'One') => 0,
9
+ Option.new(:title => 'Two') => 4,
10
+ Option.new(:title => 'Three') => 6 }
11
+ poll = Poll.create(:title => 'Test Poll', :options => options.keys)
12
+ options.each do |option, response_count|
13
+ response_count.times do
14
+ poll.submit_response option
15
+ end
16
+ end
17
+ Poll.create(:title => 'Current Poll',
18
+ :start_date => Date.today,
19
+ :options => [ Option.new(:title => 'Foo'),
20
+ Option.new(:title => 'Bar'),
21
+ Option.new(:title => 'Baz') ])
22
+ end
23
+
24
+ describe '<r:poll> with no start dates' do
25
+
26
+ before do
27
+ Poll.update_all('start_date = NULL', 'start_date IS NOT NULL')
28
+ end
29
+
30
+ it 'should not accept an empty tag' do
31
+ tag = %{<r:poll/>}
32
+ pages(:home).should render(tag).with_error("'title' attribute required")
33
+ end
34
+
35
+ end
36
+
37
+ describe 'with no polls' do
38
+
39
+ before do
40
+ Poll.delete_all
41
+ end
42
+
43
+ describe '<r:poll>' do
44
+
45
+ it 'should accept a bare tag and generate an error message' do
46
+ tag = %{<r:poll/>}
47
+ expected = 'No polls found'
48
+ pages(:home).should render(tag).as(expected)
49
+ end
50
+
51
+ it 'should accept a title attribute and generate an error message' do
52
+ tag = %{<r:poll title="Test Poll"/>}
53
+ expected = 'No polls found'
54
+ pages(:home).should render(tag).as(expected)
55
+ end
56
+
57
+ end
58
+
59
+ describe '<r:poll:title>' do
60
+
61
+ it 'should generate an error message and no inner content' do
62
+ tag = %{<r:poll><h2><r:title/></h2></r:poll>}
63
+ expected = 'No polls found'
64
+ pages(:home).should render(tag).as(expected)
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ describe '<r:poll>' do
72
+
73
+ it 'should accept an empty tag and generate no output' do
74
+ tag = %{<r:poll/>}
75
+ expected = ''
76
+ pages(:home).should render(tag).as(expected)
77
+ end
78
+
79
+ it 'should accept a title attribute and generate no output' do
80
+ tag = %{<r:poll title="Test Poll"/>}
81
+ expected = ''
82
+ pages(:home).should render(tag).as(expected)
83
+ end
84
+
85
+ it 'should accept an id tag and generate no output' do
86
+ tag = %{<r:poll id="#{Poll.find(:first).id}"/>}
87
+ expected = ''
88
+ pages(:home).should render(tag).as(expected)
89
+ end
90
+
91
+ end
92
+
93
+ describe '<r:poll:title>' do
94
+
95
+ it 'should show the title of the specified poll' do
96
+ title = 'Test Poll'
97
+ tag = %{<r:poll title="#{title}"><r:title/></r:poll>}
98
+ pages(:home).should render(tag).as(Poll.marker + title)
99
+ end
100
+
101
+ it 'should show the title of the current poll' do
102
+ title = 'Current Poll'
103
+ tag = %{<r:poll><r:title/></r:poll>}
104
+ pages(:home).should render(tag).as(Poll.marker + title)
105
+ end
106
+
107
+ end
108
+
109
+ describe '<r:poll:form>' do
110
+
111
+ it 'should generate an empty form' do
112
+ tag = %{<r:poll title="Test Poll"><r:form/></r:poll>}
113
+ expected = %r{<form action="/pages/\d+/poll_response" method="post" id="poll_form">\s*<div>\s*<input type="hidden"[^>]*>\s*</div>\s*</form>}
114
+ pages(:home).should render(tag).matching(expected)
115
+ end
116
+
117
+ end
118
+
119
+ describe '<r:poll:form:submit>' do
120
+
121
+ it 'should generate an empty form with a default submit button' do
122
+ tag = %{<r:poll title="Test Poll"><r:form><r:submit/></r:form></r:poll>}
123
+ expected = %r{<form action="/pages/\d+/poll_response" method="post" id="poll_form">\s*<div>\s*<input type="submit".*?value="Submit"[^>]*>\s*<input type="hidden"[^>]*>\s*</div>\s*</form>}
124
+ pages(:home).should render(tag).matching(expected)
125
+ end
126
+
127
+ it 'should generate an empty form with a custom submit button' do
128
+ tag = %{<r:poll title="Test Poll"><r:form><r:submit value="Vote"/></r:form></r:poll>}
129
+ expected = %r{<form action="/pages/\d+/poll_response" method="post" id="poll_form">\s*<div>\s*<input type="submit".*?value="Vote"[^>]*>\s*<input type="hidden"[^>]*>\s*</div>\s*</form>}
130
+ pages(:home).should render(tag).matching(expected)
131
+ end
132
+
133
+ end
134
+
135
+ describe '<r:poll:form:options>' do
136
+
137
+ it 'should generate an empty form' do
138
+ tag = %{<r:poll title="Test Poll"><r:form><r:options/></r:form></r:poll>}
139
+ expected = %r{<form action="/pages/\d+/poll_response" method="post" id="poll_form">\s*<div>\s*<input type="hidden"[^>]*>\s*</div>\s*</form>}
140
+ pages(:home).should render(tag).matching(expected)
141
+ end
142
+
143
+ end
144
+
145
+ describe '<r:poll:form:options:each>' do
146
+
147
+ it 'should generate an empty form' do
148
+ tag = %{<r:poll title="Test Poll"><r:form><r:options:each/></r:form></r:poll>}
149
+ expected = %r{<form action="/pages/\d+/poll_response" method="post" id="poll_form">\s*<div>\s*<input type="hidden"[^>]*>\s*</div>\s*</form>}
150
+ pages(:home).should render(tag).matching(expected)
151
+ end
152
+
153
+ end
154
+
155
+ describe '<r:poll:form:options:each:input>' do
156
+
157
+ it 'should generate a form with input options' do
158
+ tag = %{<r:poll title="Test Poll"><r:form><r:options:each><r:input/></r:options:each></r:form></r:poll>}
159
+ expected = %r{<form action="/pages/\d+/poll_response" method="post" id="poll_form">\s*<div>\s*(<input type="radio"[^>]*>\s*){3}<input type="hidden"[^>]*>\s*</div>\s*</form>}
160
+ pages(:home).should render(tag).matching(expected)
161
+ end
162
+
163
+ end
164
+
165
+ describe '<r:poll:form:options:each:title>' do
166
+
167
+ it 'should show the option titles' do
168
+ tag = %{<r:poll title="Test Poll"><r:form><r:options:each><r:input/><r:title/></r:options:each></r:form></r:poll>}
169
+ expected = %r{<form action="/pages/\d+/poll_response" method="post" id="poll_form">\s*<div>\s*(<input type="radio"[^>]*>\s*(One|Two|Three)\s*){3}<input type="hidden"[^>]*>\s*</div>\s*</form>}
170
+ pages(:home).should render(tag).matching(expected)
171
+ end
172
+
173
+ end
174
+
175
+ describe '<r:poll:options:each:title>' do
176
+
177
+ it 'should show the option titles in random order' do
178
+ tag = %{<r:poll title="Test Poll"><r:options order="rand"><r:each><r:title/></r:each></r:options></r:poll>}
179
+ expected = /OneTwoThree|OneThreeTwo|TwoOneThree|TwoThreeOne|ThreeOneTwo|ThreeTwoOne/
180
+ pages(:home).should render(tag).matching(expected)
181
+ end
182
+
183
+ end
184
+
185
+ describe '<r:poll:options:each:number_responses>' do
186
+
187
+ it 'should show the response numbers in descending order' do
188
+ tag = %{<r:poll title="Test Poll"><r:options order="desc"><r:each><p><r:number_responses/></p></r:each></r:options></r:poll>}
189
+ expected = Poll.marker + '<p>6</p><p>4</p><p>0</p>'
190
+ pages(:home).should render(tag).as(expected)
191
+ end
192
+
193
+ end
194
+
195
+ describe '<r:poll:options:each:percent_responses>' do
196
+
197
+ it 'should show the response percentages in ascending order' do
198
+ tag = %{<r:poll title="Test Poll"><r:options order="asc"><r:each><p><r:percent_responses/></p></r:each></r:options></r:poll>}
199
+ expected = Poll.marker + '<p>0.0</p><p>40.0</p><p>60.0</p>'
200
+ pages(:home).should render(tag).as(expected)
201
+ end
202
+
203
+ end
204
+
205
+ describe '<r:poll:options:each:if_first>' do
206
+
207
+ it 'should render inner tags only if the current context is the first option' do
208
+ tag = %{<r:poll title="Test Poll"><ul><r:options order="desc"><r:each><li<r:if_first> class="first"</r:if_first>><r:percent_responses /></li></r:each></r:options></ul></r:poll>}
209
+ expected = Poll.marker + '<ul><li class="first">60.0</li><li>40.0</li><li>0.0</li></ul>'
210
+ pages(:home).should render(tag).as(expected)
211
+ end
212
+
213
+ end
214
+
215
+ describe '<r:poll:options:each:if_last>' do
216
+
217
+ it 'should render inner tags only if the current context is the last option' do
218
+ tag = %{<r:poll title="Test Poll"><ul><r:options order="desc"><r:each><li<r:if_last> class="last"</r:if_last>><r:percent_responses /></li></r:each></r:options></ul></r:poll>}
219
+ expected = Poll.marker + '<ul><li>60.0</li><li>40.0</li><li class="last">0.0</li></ul>'
220
+ pages(:home).should render(tag).as(expected)
221
+ end
222
+
223
+ end
224
+
225
+ describe '<r:poll:options:each:unless_first>' do
226
+
227
+ it 'should render inner tags only unless the current context is the first option' do
228
+ tag = %{<r:poll title="Test Poll"><r:options order="desc"><r:each><r:unless_first>,</r:unless_first><r:percent_responses /></r:each></r:options></r:poll>}
229
+ expected = Poll.marker + '60.0,40.0,0.0'
230
+ pages(:home).should render(tag).as(expected)
231
+ end
232
+
233
+ end
234
+
235
+ describe '<r:poll:options:each:unless_last>' do
236
+
237
+ it 'should render inner tags only unless the current context is the last option' do
238
+ tag = %{<r:poll title="Test Poll"><r:options order="desc"><r:each><r:percent_responses /><r:unless_last>,</r:unless_last></r:each></r:options></r:poll>}
239
+ expected = Poll.marker + '60.0,40.0,0.0'
240
+ pages(:home).should render(tag).as(expected)
241
+ end
242
+
243
+ end
244
+
245
+ ## Test poll archives
246
+ describe '<r:polls>' do
247
+
248
+ it 'should accept an empty tag and generate no output' do
249
+ tag = %{<r:polls/>}
250
+ expected = ''
251
+ pages(:home).should render(tag).as(expected)
252
+ end
253
+
254
+ it 'should accept valid options and generate no output' do
255
+ tag = %{<r:polls per_page="10" by="title" order="asc" show_current="false" />}
256
+ expected = ''
257
+ pages(:home).should render(tag).as(expected)
258
+ end
259
+
260
+ describe 'with per_page option' do
261
+
262
+ it 'should not accept a non-positive integer and generate an error' do
263
+ tag = %{<r:polls per_page="-5" />}
264
+ pages(:home).should render(tag).with_error('the per_page attribute of the polls tag must be a positive integer')
265
+ end
266
+
267
+ it 'should not accept a non-integer value and generate an error' do
268
+ tag = %{<r:polls per_page="no" />}
269
+ pages(:home).should render(tag).with_error('the per_page attribute of the polls tag must be a positive integer')
270
+ end
271
+
272
+ end
273
+
274
+ describe 'with by option' do
275
+
276
+ it 'should not accept an invalid field name and generate an error' do
277
+ tag = %{<r:polls by="foobar"/>}
278
+ pages(:home).should render(tag).with_error('the by attribute of the polls tag must specify a valid field name')
279
+ end
280
+
281
+ end
282
+
283
+ describe 'with order option' do
284
+
285
+ it 'should accept "asc" and generate no output' do
286
+ tag = %{<r:polls order="asc"/>}
287
+ expected = ''
288
+ pages(:home).should render(tag).as(expected)
289
+ end
290
+
291
+ it 'should accept "desc" and generate no output' do
292
+ tag = %{<r:polls order="desc"/>}
293
+ expected = ''
294
+ pages(:home).should render(tag).as(expected)
295
+ end
296
+
297
+ it 'should not accept a value other than "asc" or "desc" and generate an error' do
298
+ tag = %{<r:polls order="xxx"/>}
299
+ pages(:home).should render(tag).with_error('the order attribute of the polls tag must be either "asc" or "desc"')
300
+ end
301
+
302
+ end
303
+
304
+ describe 'with no polls' do
305
+
306
+ before do
307
+ Poll.delete_all
308
+ end
309
+
310
+ it 'should accept a bare tag and generate an error message' do
311
+ tag = %{<r:polls/>}
312
+ expected = 'No polls found'
313
+ pages(:home).should render(tag).as(expected)
314
+ end
315
+
316
+ end
317
+
318
+ end
319
+
320
+ describe '<r:polls:each>' do
321
+
322
+ it 'should generate no output' do
323
+ tag = %{<r:polls><r:each></r:each></r:polls>}
324
+ expected = ''
325
+ pages(:home).should render(tag).as(expected)
326
+ end
327
+
328
+ end
329
+
330
+ describe '<r:polls:each:poll>' do
331
+
332
+ it 'should generate no output' do
333
+ tag = %{<r:polls><r:each><r:poll/></r:each></r:polls>}
334
+ expected = ''
335
+ pages(:home).should render(tag).as(expected)
336
+ end
337
+
338
+ end
339
+
340
+ describe '<r:polls:each:poll:title>' do
341
+
342
+ before do
343
+ # Create a future poll that should never appear in any results
344
+ Poll.create(:title => 'Next Poll', :start_date => Date.today + 1.week)
345
+ end
346
+
347
+ it 'should generate an ordered list of poll titles in descending order' do
348
+ tag = %{<r:polls order="desc" show_current="true"><r:each><r:poll><p><r:title/></p></r:poll></r:each></r:polls>}
349
+ expected = "#{Poll.marker}<p>Test Poll</p><p>Current Poll</p>"
350
+ pages(:home).should render(tag).as(expected)
351
+ end
352
+
353
+ it 'should generate a list of the one poll that has a non-future start date' do
354
+ tag = %{<r:polls by="start_date" show_current="true"><r:each><r:poll><p><r:title/></p></r:poll></r:each></r:polls>}
355
+ expected = "#{Poll.marker}<p>Current Poll</p>"
356
+ pages(:home).should render(tag).as(expected)
357
+ end
358
+
359
+ it 'should generate an ordered list of poll titles in descending order by start date' do
360
+ poll = Poll.find_by_title('Test Poll').update_attribute(:start_date, Date.today - 1.week)
361
+ tag = %{<r:polls by="start_date" order="desc" show_current="true"><r:each><r:poll><p><r:title/></p></r:poll></r:each></r:polls>}
362
+ expected = "#{Poll.marker}<p>Current Poll</p><p>Test Poll</p>"
363
+ pages(:home).should render(tag).as(expected)
364
+ end
365
+
366
+ end
367
+
368
+ describe '<r:polls:each:poll:options:title>' do
369
+
370
+ it 'should generate a list containing the current poll with its options' do
371
+ tag = %{<r:polls by="start_date" show_current="true"><r:each><r:poll><p><r:title/></p><r:options order="desc"><r:each><p><r:title/></p></r:each></r:options></r:poll></r:each></r:polls>}
372
+ expected = "#{Poll.marker}<p>Current Poll</p><p>Foo</p><p>Bar</p><p>Baz</p>"
373
+ pages(:home).should render(tag).as(expected)
374
+ end
375
+
376
+ it 'should generate no output when sorting by start date and excluding the current poll' do
377
+ tag = %{<r:polls by="start_date"><r:each><r:poll><p><r:title/></p><r:options order="desc"><r:each><p><r:title/></p></r:each></r:options></r:poll></r:each></r:polls>}
378
+ expected = ''
379
+ pages(:home).should render(tag).as(expected)
380
+ end
381
+
382
+ end
383
+
384
+ describe '<r:polls:pages>' do
385
+
386
+ describe 'with less than per_page polls' do
387
+
388
+ it 'should not generate pagination links' do
389
+ tag = %{<r:polls by="title"><r:pages/></r:polls>}
390
+ expected = ''
391
+ pages(:home).should render(tag).as(expected)
392
+ end
393
+
394
+ end
395
+
396
+ describe 'with more than per_page polls' do
397
+
398
+ before do
399
+ (1..20).each do |i|
400
+ Poll.create(:title => "Poll #{i}",
401
+ :start_date => Date.today - i.weeks,
402
+ :options => [ Option.new(:title => "Foo #{i}"),
403
+ Option.new(:title => "Bar #{i}"),
404
+ Option.new(:title => "Baz #{i}") ])
405
+ end
406
+ end
407
+
408
+ it 'should generate pagination links' do
409
+ tag = %{<r:polls per_page="5" by="start_date" order="desc"><r:pages/></r:polls>}
410
+ expected = %r{<span class="current">1</span>}
411
+ pages(:home).should render(tag).matching(expected)
412
+ end
413
+ end
414
+ end
415
+ end