Then we create a GET handler for this.
And this is the Pl/SQL code. Do bear in mind that here, averages and medians only make sense because the answers in the poll are numerical. In any case, tables are joined and columns are grouped to get these averages and medians. Before using the AVG() and MEDIAN() functions, we must first run TO_NUMBER() on the data, to convert them to numerical values.
SELECT
pq.TITLE,
AVG(TO_NUMBER(pr.RESULT)) AS AVG_RESULT,
MEDIAN(TO_NUMBER(pr.RESULT)) AS MEDIAN_RESULT
FROM POLL_QUESTIONS pq LEFT JOIN POLL_RESULTS pr
ON pr.QUESTION_SERIAL_NO = pq.SERIAL_NO AND pr.POLL_ID = :id AND pq.POLL_ID = :id
GROUP BY pq.TITLE
ORDER BY AVG_RESULT DESC, MEDIAN_RESULT DESC
pq.TITLE,
AVG(TO_NUMBER(pr.RESULT)) AS AVG_RESULT,
MEDIAN(TO_NUMBER(pr.RESULT)) AS MEDIAN_RESULT
FROM POLL_QUESTIONS pq LEFT JOIN POLL_RESULTS pr
ON pr.QUESTION_SERIAL_NO = pq.SERIAL_NO AND pr.POLL_ID = :id AND pq.POLL_ID = :id
GROUP BY pq.TITLE
ORDER BY AVG_RESULT DESC, MEDIAN_RESULT DESC
The results should be something like this.
{
"items": [
{
"title": "IT CAME UPON THE MIDNIGHT CLEAR",
"avg_result": 4,
"median_result": 5
},
{
"title": "JINGLE BELL ROCK",
"avg_result": 4,
"median_result": 4.5
},
{
"title": "SILENT NIGHT",
"avg_result": 3.5,
"median_result": 3
},
{
"title": "THE FIRST NOEL",
"avg_result": 3.16666666666666666666666666666666666667,
"median_result": 3
},
{
"title": "JINGLE BELLS",
"avg_result": 3,
"median_result": 3
},
{
"title": "HARK THE HERALD ANGELS SING",
"avg_result": 2.83333333333333333333333333333333333333,
"median_result": 3
},
{
"title": "DECK THE HALLS",
"avg_result": 2.66666666666666666666666666666666666667,
"median_result": 3
},
{
"title": "JOY TO THE WORLD",
"avg_result": 2.54545454545454545454545454545454545455,
"median_result": 3
},
{
"title": "RUDOLPH THE RED-NOSED REINDEER",
"avg_result": 2.5,
"median_result": 2.5
},
{
"title": "I SAW MOMMY KISSING SANTA CLAUS",
"avg_result": 2.27272727272727272727272727272727272727,
"median_result": 2
}
],
"hasMore": false,
"limit": 25,
"offset": 0,
"count": 10,
"links": [
{
"rel": "self",
"href": "https://oracleapex.com/ords/teochewthunder/polls/poll/1/results/"
},
{
"rel": "describedby",
"href": "https://oracleapex.com/ords/teochewthunder/metadata-catalog/polls/poll/1/results/"
},
{
"rel": "first",
"href": "https://oracleapex.com/ords/teochewthunder/polls/poll/1/results/"
}
]
}
"items": [
{
"title": "IT CAME UPON THE MIDNIGHT CLEAR",
"avg_result": 4,
"median_result": 5
},
{
"title": "JINGLE BELL ROCK",
"avg_result": 4,
"median_result": 4.5
},
{
"title": "SILENT NIGHT",
"avg_result": 3.5,
"median_result": 3
},
{
"title": "THE FIRST NOEL",
"avg_result": 3.16666666666666666666666666666666666667,
"median_result": 3
},
{
"title": "JINGLE BELLS",
"avg_result": 3,
"median_result": 3
},
{
"title": "HARK THE HERALD ANGELS SING",
"avg_result": 2.83333333333333333333333333333333333333,
"median_result": 3
},
{
"title": "DECK THE HALLS",
"avg_result": 2.66666666666666666666666666666666666667,
"median_result": 3
},
{
"title": "JOY TO THE WORLD",
"avg_result": 2.54545454545454545454545454545454545455,
"median_result": 3
},
{
"title": "RUDOLPH THE RED-NOSED REINDEER",
"avg_result": 2.5,
"median_result": 2.5
},
{
"title": "I SAW MOMMY KISSING SANTA CLAUS",
"avg_result": 2.27272727272727272727272727272727272727,
"median_result": 2
}
],
"hasMore": false,
"limit": 25,
"offset": 0,
"count": 10,
"links": [
{
"rel": "self",
"href": "https://oracleapex.com/ords/teochewthunder/polls/poll/1/results/"
},
{
"rel": "describedby",
"href": "https://oracleapex.com/ords/teochewthunder/metadata-catalog/polls/poll/1/results/"
},
{
"rel": "first",
"href": "https://oracleapex.com/ords/teochewthunder/polls/poll/1/results/"
}
]
}
Back to the Rails server! In the CLI, run this command to create the controller result.
rails generate controller result
Once we've done that, it's time to modify the generated file. Again, we need httparty. We'll reuse the environment variable ORDS_API_URL but append "/results" to it.
app/controllers/result_controller.rb
require "httparty"
class ResultController < ApplicationController
ORDS_API_URL = ENV["ORDS_API_URL"] + "/results"
def index
end
end
class ResultController < ApplicationController
ORDS_API_URL = ENV["ORDS_API_URL"] + "/results"
def index
end
end
Of course, the next step is to mirror what we did for the index action of the root page, and use HTTParty's get() method to call the URL endpoint, then have an If block to handle the result.
app/controllers/result_controller.rb
require "httparty"
class ResultController < ApplicationController
ORDS_API_URL = ENV["ORDS_API_URL"] + "/results"
def index
response = HTTParty.get(
ORDS_API_URL
)
if response.code == 200
else
flash.now[:alert] = "Error fetching data."
@api_data = {}
end
end
end
class ResultController < ApplicationController
ORDS_API_URL = ENV["ORDS_API_URL"] + "/results"
def index
response = HTTParty.get(
ORDS_API_URL
)
if response.code == 200
else
flash.now[:alert] = "Error fetching data."
@api_data = {}
end
end
end
If successful, we return the result to the view by binding it to api_data.
app/controllers/result_controller.rb
require "httparty"
class ResultController < ApplicationController
ORDS_API_URL = ENV["ORDS_API_URL"] + "/results"
def index
response = HTTParty.get(
ORDS_API_URL
)
if response.code == 200
@api_data = response.parsed_response
else
flash.now[:alert] = "Error fetching data."
@api_data = {}
end
end
end
class ResultController < ApplicationController
ORDS_API_URL = ENV["ORDS_API_URL"] + "/results"
def index
response = HTTParty.get(
ORDS_API_URL
)
if response.code == 200
@api_data = response.parsed_response
else
flash.now[:alert] = "Error fetching data."
@api_data = {}
end
end
end
This is the view. We have a header, a div styled using the CSS class pollresult, and a table.
app/views/result/index.html.erb
<h1>Poll Results</h1>
<div class="pollresult">
<table>
<tr>
<td width="300px"><b>Carol</b></td><td width="150px" style="text-align: right;"><b>Average Rating</b></td>
<td width="150px" style="text-align: right;"><b>Median Rating</b></td>
</tr>
</table>
</div>
<div class="pollresult">
<table>
<tr>
<td width="300px"><b>Carol</b></td><td width="150px" style="text-align: right;"><b>Average Rating</b></td>
<td width="150px" style="text-align: right;"><b>Median Rating</b></td>
</tr>
</table>
</div>
Refer to the sample JSON result I showed you earlier. We'll use the title, avg_result and median_result properties here.
app/views/result/index.html.erb
<h1>Poll Results</h1>
<div class="pollresult">
<table>
<tr>
<td width="300px"><b>Carol</b></td><td width="150px" style="text-align: right;"><b>Average Rating</b></td>
<td width="150px" style="text-align: right;"><b>Median Rating</b></td>
</tr>
<% @api_data["items"].each do |result| %>
<tr>
<td><%= "#{result['title']}" %></td>
<td style="text-align: right;"><%= "#{result['avg_result']}" %></td>
<td style="text-align: right;"><%= "#{result['median_result']}" %></td>
</tr>
<% end %>
</table>
</div>
<div class="pollresult">
<table>
<tr>
<td width="300px"><b>Carol</b></td><td width="150px" style="text-align: right;"><b>Average Rating</b></td>
<td width="150px" style="text-align: right;"><b>Median Rating</b></td>
</tr>
<% @api_data["items"].each do |result| %>
<tr>
<td><%= "#{result['title']}" %></td>
<td style="text-align: right;"><%= "#{result['avg_result']}" %></td>
<td style="text-align: right;"><%= "#{result['median_result']}" %></td>
</tr>
<% end %>
</table>
</div>
For the CSS, if you're a lazy bastard like me, you might just want to piggyback off this existing class, or create a new one with a custom design.
app/assets/stylesheets/application.css
.pollform, .pollresult
{
width: 600px;
padding: 10px;
border-radius: 10px;
border: 3px solid rgb(200, 0, 0);
background-color: rgb(0, 200, 0);
margin: 5% auto 0 auto;
}
{
width: 600px;
padding: 10px;
border-radius: 10px;
border: 3px solid rgb(200, 0, 0);
background-color: rgb(0, 200, 0);
margin: 5% auto 0 auto;
}
Do you see the problem? The average is way too long and needs to be truncated.
We use sprintf(), passing in "%.1f" to truncate the text to one decimal place.
app/views/result/index.html.erb
<h1>Poll Results</h1>
<div class="pollresult">
<table>
<tr>
<td width="300px"><b>Carol</b></td><td width="150px" style="text-align: right;"><b>Average Rating</b></td>
<td width="150px" style="text-align: right;"><b>Median Rating</b></td>
</tr>
<% @api_data["items"].each do |result| %>
<tr>
<td><%= "#{result['title']}" %></td>
<td style="text-align: right;"><%= "#{sprintf('%.1f', result['avg_result'])}" %></td>
<td style="text-align: right;"><%= "#{sprintf('%.1f', result['median_result'])}" %></td>
</tr>
<% end %>
</table>
</div>
<div class="pollresult">
<table>
<tr>
<td width="300px"><b>Carol</b></td><td width="150px" style="text-align: right;"><b>Average Rating</b></td>
<td width="150px" style="text-align: right;"><b>Median Rating</b></td>
</tr>
<% @api_data["items"].each do |result| %>
<tr>
<td><%= "#{result['title']}" %></td>
<td style="text-align: right;"><%= "#{sprintf('%.1f', result['avg_result'])}" %></td>
<td style="text-align: right;"><%= "#{sprintf('%.1f', result['median_result'])}" %></td>
</tr>
<% end %>
</table>
</div>
There you go!
For a final touch, add a nav link to each of these pages.
app/views/poll/index.html.erb
<h1><%= @api_data["items"][0]["name"] %></h1>
<h2><%= link_to "View Results", result_page_path %></h2>
<div class="pollform">
<h2><%= link_to "View Results", result_page_path %></h2>
<div class="pollform">
app/views/result/index.html.erb
<h1>Poll Results</h1>
<h2><%= link_to "View Poll", root_path %></h2>
<div class="pollresult">
<h2><%= link_to "View Poll", root_path %></h2>
<div class="pollresult">
Then style the h2 tag.
app/assets/stylesheets/application.css
h1
{
text-align: center;
color: rgba(255, 255, 255, 0.5);
}
h2
{
text-align: center;
font-size: 0.8em;
}
.pollform, .pollresult
{
width: 600px;
padding: 10px;
border-radius: 10px;
border: 3px solid rgb(200, 0, 0);
background-color: rgb(0, 200, 0);
margin: 5% auto 0 auto;
}
{
text-align: center;
color: rgba(255, 255, 255, 0.5);
}
h2
{
text-align: center;
font-size: 0.8em;
}
.pollform, .pollresult
{
width: 600px;
padding: 10px;
border-radius: 10px;
border: 3px solid rgb(200, 0, 0);
background-color: rgb(0, 200, 0);
margin: 5% auto 0 auto;
}
Unit Tests
It's time to write a few unit tests! Bear in mind that these are just samples. There are probably better tests one can write. This is the default test that is generated when we create a controller.test/controllers/poll_controller_test.rb
require "test_helper"
class PollControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get poll_page_url
assert_response :success
assert_not_nil assigns(:api_data)
end
end
class PollControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get poll_page_url
assert_response :success
assert_not_nil assigns(:api_data)
end
end
And more. Here, we test submitting the form by sending a sample payload, and checking a couple of expected behaviors. We check if there's a redirect to the form page, and that there's a flash notice.
test/controllers/poll_controller_test.rb
require "test_helper"
class PollControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get poll_page_url
assert_response :success
assert_not_nil assigns(:api_data)
end
test "should submit results" do
post submit_poll_form_url, params: { answers: { "1" => "3", "2" => "5", "3" => "2"} }
assert_response :redirect
assert_redirected_to root_path
assert_not_nil flash[:notice]
end
end
class PollControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get poll_page_url
assert_response :success
assert_not_nil assigns(:api_data)
end
test "should submit results" do
post submit_poll_form_url, params: { answers: { "1" => "3", "2" => "5", "3" => "2"} }
assert_response :redirect
assert_redirected_to root_path
assert_not_nil flash[:notice]
end
end
test/controllers/result_controller_test.rb
require "test_helper"
class ResultControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get result_page_url
assert_response :success
assert_not_nil assigns(:api_data)
end
end
class ResultControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get result_page_url
assert_response :success
assert_not_nil assigns(:api_data)
end
end
When you run these tests, that's what you should get.
Conclusion
Looks like it's the day after Christmas.This concludes my first Ruby On Rails web tutorial in years. I like to think it's better than my last couple efforts. That's because despite being an old fart, I'm still growing and adapting, and so should you. Enjoy your holidays!
Poll-ite season's greetings!
T___T
T___T






No comments:
Post a Comment