How to reduce rails code efficiently? - ruby

In Projects controller I have 5 methods:
def day1
end
def day2
end
def day3
end
def day4
end
def day5
end
In the views I have:
day1.html.erb
day2.html.erb
day3.html.erb
day4.html.erb
day5.html.erb
In each individual view, I have a block of code similar to this:
In day1.html.erb:
<% if current_user.speed? || current_user.admin? %>
"Display Day 1"
<% else %>
<% unless current_user.day_count.nil? %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= 1 %>
"Display Day 1"
<% else %>
"This project cannot open now. Please wait until day1!"
<% end %>
<% end %>
<% end %>
In day2.html.erb:
<% if current_user.speed? || current_user.admin? %>
"Display Day 2"
<% else %>
<% unless current_user.day_count.nil? %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= 2 %>
"Display Day 2"
<% else %>
"This project cannot open now. Please wait until day2!"
<% end %>
<% end %>
<% end %>
and so on, so on... until day 5.
In the routes.rb:
match 'projects/day-1' => "projects#day1", :via => [:get], as: "day1"
match 'projects/day-2' => "projects#day2", :via => [:get], as: "day2"
match 'projects/day-3' => "projects#day3", :via => [:get], as: "day3"
match 'projects/day-4' => "projects#day4", :via => [:get], as: "day4"
match 'projects/day-5' => "projects#day5", :via => [:get], as: "day5"
And in index.html.erb views I have:
<% if current_user.speed? || current_user.admin? %>
<div class="day_tracking">
<div class="day_passed"><span>✔</span><p>1</p></div>
<div class="day_passed"><span>✔</span><p>2</p></div>
<div class="day_passed"><span>✔</span><p>3</p></div>
<div class="day_passed"><span>✔</span><p>4</p></div>
<div class="day_passed"><span>✔</span><p>5</p></div>
</div>
<% else %>
<% unless current_user.day_count.nil? %>
<div class="day_tracking">
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= 1 %>
<div class="day_passed"><span>✔</span><p>1</p></div>
<% else %>
<div class="day_left"><span>✘</span><p>1</p></div>
<% end %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= 2 %>
<div class="day_passed"><span>✔</span><p>2</p></div>
<% else %>
<div class="day_left"><span>✘</span><p>2</p></div>
<% end %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= 3 %>
<div class="day_passed"><span>✔</span><p>3</p></div>
<% else %>
<div class="day_left"><span>✘</span><p>3</p></div>
<% end %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= 4 %>
<div class="day_passed"><span>✔</span><p>4</p></div>
<% else %>
<div class="day_left"><span>✘</span><p>4</p></div>
<% end %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= 5 %>
<div class="day_passed"><span>✔</span><p>5</p></div>
<% else %>
<div class="day_left"><span>✘</span><p>5</p></div>
<% end %>
</div>
<% end %>
<% end %>
What the code does is very simple. First, I check to see if the current_user is an admin or a speed user or not. If he is, then display:
✔1 ✔2 ✔3 ✔4 ✔5
( It means he can see all the projects )
Else, If the current_user is not a speed user or an admin, then display:
✔1 ✘2 ✘3 ✘4 ✘5
( It means he can only see the number of projects
equivalent to his day_count ---- in case day_count not nil!)
For Example:
If the current_user have day_count = 1, then he can only see project 1:
✔1 ✘2 ✘3 ✘4 ✘5
If the curent_user have day_count = 2, then he can only see project 1 and 2:
✔1 ✔2 ✘3 ✘4 ✘5
... And so on, so on....
The code is simple, but quite repetitive. I know it's a bad way of coding , but still cannot figure out how to reduce it efficiently and effectively. If I have more than 30 days, the code can go on, go on to more than 2,000 lines of code and probably will make the program become slow down!
Can anyone help me fix this? Thanks in advance.

routes
match 'projects/days/(:day)' => "projects#day", :via => [:get], as: "day"
views
day.html.erb
<% if current_user.speed? || current_user.admin? %>
"Display Day #{#day}"
<% else %>
<% unless current_user.day_count.nil? %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= #day %>
"Display Day #{#day}"
<% else %>
"This project cannot open now. Please wait until day#{#day}!"
<% end %>
<% end %>
controller
def day
#total_days = 5
#day = params[:day]
end
index.html
<% if current_user.speed? || current_user.admin? %>
<div class="day_tracking">
<% #total_days.each do |day| %>
<div class="day_passed"><span>✔</span><p><%= day %></p></div>
</div>
<% else %>
<% unless current_user.day_count.nil? %>
<div class="day_tracking">
<% #total_days.each do |day| %>
<% if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= day %>
<div class="day_passed"><span>✔</span><p><%= day %></p></div>
<% else %>
<div class="day_left"><span>✘</span><p><%= day %></p></div>
<% end %>
<% end %>
</div>
<% end %>
<% end %>
You can like this. There are more refactoring point, It is just concept.

It can be easily re-factored. One of the main tenets of Ruby is the DRY principle and your design goes against it.
Change your route according to this.
match 'projects/:days' => "projects#show_project", :via => [:get]
In your controller you should have this.
def show_project
end
Transfer the business logic to the project model.
def has_access?(no_of_days)
return true if current_user.speed? || current_user.admin?
unless current_user.day_count.nil?
if (Time.now.to_date - current_user.day_count.to_date).to_i + 1 >= no_of_days
true
else
false
end
end
Now in your view its as simple as.
<% if #project.has_access?(params[:days]) %>
"Display Day #{params[:days]}"
<% else %>
"This project cannot open now. Please wait until day#{params[:days]}!"
<% end %>

Related

How would I get all people, that are following my in Ruby

I am using the act_as_follower gem, and I get all the users, that I follow, but now I want all the users, that are following me, how would I do that. I checked and I think something like this
current_user.followers, but I cant get it working. My code for people that I am following is this:
<% #users = User.all %>
<% #users.each do |user| %>
<% unless user == current_user %>
<div class="container people-lists list-following">
<%= image_tag("/assets/" + user.avatar + ".png", alt: "Logo", class: "people-image") %>
<%= user.username %>
<% if user_signed_in? %>
<% unless user == current_user %>
<% if current_user.following?(user) %>
<%= button_to("Un-Follow", user_follow_path(user.to_param, current_user.get_follow(user).id), :method => :delete, :remote => true, class: 'btn btn-primary button') %>
<% else %>
<%= button_to("Follow", user_follows_path(user.to_param), :remote => true, class: 'btn btn-primary button') %>
<% end %>
<% end %>
<% end %>
</div>
<% end %>
<% end %>
I found I the User.all returns all of them, and I can use the all_following, and follows like this and it works.
<% #users = current_user.all_following %>
And like this
<% #users = current_user.followers %>

Why one method work perfectly for one action and do not work for another?

I'm newbee in rails, so could you explain why method survey_type works for this (attempts/new):
<h2 class="survey-title">
<%= #survey.name %>
<p><small><%= #attempt.survey.description %></small></p>
</h2>
<%= form_for(#attempt, url: attempt_scope(#attempt)) do |f| %>
<%= hidden_field_tag :survey_id, #survey.id %>
<ol class="questions">
<% if is_multanswer?(#survey.survey_type) %>
<%= f.fields_for :answers, get_answer_fields(#attempt) do |answer_fields| %>
<li>
<% question = answer_fields.object.question %>
<p class="question"><%= question.text %></p>
<ul class="options">
<%= collection_check_boxes('survey_attempt[answers_attributes]', question.id, question.options, :id, :text) do |b| %>
<li class="checkbox">
<%= b.label { b.check_box + b.text } %>
</li>
<% end %>
</ul>
</li>
<% end -%>
<% else %>
<%= f.fields_for :answers, get_answer_fields(#attempt) do |answer_fields| %>
<li>
<% question = answer_fields.object.question %>
<p class="question"><%= question.text %></p>
<ul class="options">
<%= collection_radio_buttons('survey_attempt[answers_attributes]', question.id, question.options, :id, :text) do |b| %>
<li class="radio">
<%= b.label { b.radio_button + b.text } %>
</li>
<% end %>
</ul>
</li>
<% end -%>
<% end %>
</ol>
<%= f.submit "Submit", class: 'btn btn-default' %>
<% end -%>
and do not work for this (attempts/show):
<div class="container">
<h2 class="survey-title">
<%= #attempt.survey.name %>
<p><small><%= #attempt.survey.description %></small></p>
</h2>
<ol class="questions">
<% if is_multanswer?(#survey.survey_type) %>
<% #attempt.answers.each do |answer| %>
<li>
<p class="question"> <%= answer.question.text %> </p>
<ul class="options">
<% answer.question.options.each do |option| %>
<li class="checkbox">
<label>
<%= check_box_tag '', '', the_chosen_one?(answer, option), disabled: true %>
<% color = get_color_of_option(answer, option) %>
<span class="<%= color %> <%= the_chosen_one?(answer, option) %>"> <%= option.text %> <%= get_weight(option) %> </span>
</label>
<p class="answers-number"> <%= number_of_people_who_also_answered(option.id) %> </p>
</li>
<% end %>
</ul>
</li>
<% end %>
<% else %>
<% #attempt.answers.each do |answer| %>
<li>
<p class="question"> <%= answer.question.text %> </p>
<ul class="options">
<% answer.question.options.each do |option| %>
<li class="radio">
<label>
<%= radio_button_tag '', '', the_chosen_one?(answer, option), disabled: true %>
<% color = get_color_of_option(answer, option) %>
<span class="<%= color %> <%= the_chosen_one?(answer, option) %>"> <%= option.text %> <%= get_weight(option) %> </span>
</label>
<p class="answers-number"> <%= number_of_people_who_also_answered(option.id) %> </p>
</li>
<% end %>
</ul>
</li>
<% end %>
<% end %>
here is controllers:
attempts_controller
class AttemptsController < ApplicationController
helper 'surveys'
before_filter :load_survey, only: [:new, :create]
def index
#surveys = Survey::Survey.active
end
def show
#attempt = Survey::Attempt.find_by(id: params[:id])
render :access_error if current_user.id != #attempt.participant_id
end
def new
#participant = current_user
unless #survey.nil?
#attempt = #survey.attempts.new
#attempt.answers.build
end
end
def create
#attempt = #survey.attempts.new(params_whitelist)
#attempt.participant = current_user
if #attempt.valid? && #attempt.save
correct_options_text = #survey.correct_options.present? ? 'Bellow are the correct answers marked in green' : ''
redirect_to attempt_path(#attempt.id), notice: "Thank you for answering #{#survey.name}! #{correct_options_text}"
else
build_flash(#attempt)
#participant = current_user
render :new
end
end
def delete_user_attempts
Survey::Attempt.where(participant_id: params[:user_id], survey_id: params[:survey_id]).destroy_all
redirect_to new_attempt_path(survey_id: params[:survey_id])
end
private
def load_survey
#survey = Survey::Survey.find_by(id: params[:survey_id])
end
def params_whitelist
if params[:survey_attempt]
params[:survey_attempt][:answers_attributes] = params[:survey_attempt][:answers_attributes].map { |attrs| { question_id: attrs.first, option_id: attrs.last } }
params.require(:survey_attempt).permit(Survey::Attempt::AccessibleAttributes)
end
end
def current_user
view_context.current_user
end
end
and surveys_controller:
class SurveysController < ApplicationController
before_filter :load_survey, only: [:show, :edit, :update, :destroy]
def index
type = view_context.get_survey_type(params[:type])
query = if type then Survey::Survey.where(survey_type: type) else Survey::Survey end
#surveys = query.order(created_at: :desc).page(params[:page]).per(15)
end
def new
#survey = Survey::Survey.new(survey_type: view_context.get_survey_type(params[:type]))
end
def create
#survey = Survey::Survey.new(params_whitelist)
if #survey.valid? && #survey.save
default_redirect
else
build_flash(#survey)
render :new
end
end
def edit
end
def show
end
def update
if #survey.update_attributes(params_whitelist)
default_redirect
else
build_flash(#survey)
render :edit
end
end
def destroy
#survey.destroy
default_redirect
end
private
def default_redirect
redirect_to surveys_path, notice: I18n.t("surveys_controller.#{action_name}")
end
def load_survey
#survey = Survey::Survey.find(params[:id])
end
def params_whitelist
params.require(:survey_survey).permit(Survey::Survey::AccessibleAttributes << :survey_type)
end
end
and helpers:
def get_color_of_option answer, option
if is_quiz?(answer.question.survey.survey_type)
if option.correct
'bg-success'
elsif the_chosen_one?(answer, option)
'bg-danger'
end
elsif is_score?(answer.question.survey.survey_type)
get_weight_html_class option
end
end
def get_survey_type survey_type
get_survey_types[survey_type] || get_survey_types.invert[survey_type]
end
def get_survey_types
{ 0 => 'quiz',
1 => 'score',
2 => 'poll',
3 => 'multanswer'
}
end
def is_quiz? something
something == 0 || something == 'quiz'
end
def is_score? something
something == 1 || something == 'score'
end
def is_poll? something
something == 2 || something == 'poll'
end
def is_multanswer? something
something == 3 || something == 'multanswer'
end
Thanks in advance!
In your AttemptsController you have
before_filter :load_survey, only: [:new, :create]
that sets the #survey variable for your new action, but doesn't get called for show, so that action never gets the variable set.
You can add :show into your :only conditions
before_filter :load_survey, only: [:new, :create, :show]
and that should fix things

Why isn't this simple elsif working in Rails/Ruby?

If there is a flash notice > "Flash notice"
If there isn't a flash notice and you're on the homepage > "Welcome"
Code:
<% if flash %>
<% flash.each do |name, msg| %>
<%= content_tag :span, msg, id: "flash_#{name}" %>
<% end %>
<% elsif current_page('/') %>
<% print 'Hi' %>
<% end %>
It prints the flashes correctly but not the welcome on the home page. Doesn't seem to matter if I try current_page or root_url or print 'Welcome' or just plain "Welcome" with no code wrapper. Why?
Yes, almost, I wasn't checking the flash properly at the beginning of the block. This works now:
<% if flash.present? %>
<% flash.each do |name, msg| %>
<%= content_tag :span, msg, id: "flash_#{name}" %>
<% end %>
<% elsif current_page?('/') %>
Welcome to The Spain Report.
<% end %>
Thank you all!

Only Display Current Month Data

I have created a functioning e-commerce platform where Members can buy songs and my Index page organizes the data by Month. Everything works fine, But now I would like to strictly display the information for the current month. (In Our Case JUNE).
Currently I am able to list every month's data, but this can become a problem to display when million of songs are being bought throughout the year.
How Can I Only display the current Months Data?
CONTROLLER
def index
#orders = Order.find(:all, :order => 'order_date, id')
end
VIEW
###How can i Only Display Current Month?
<% #orders.sort.group_by { |order| order.order_date.beginning_of_month }.each do |month, orders| %>
<h3><%= month.strftime('%B') %> </h3>
<% orders.group_by { |order| order.song.album.artist }.each do |artist, orders| %>
<h4><%= artist.name %> </h4>
<% orders.group_by { |order| order.song.album }.each do |album, orders| %>
<h5><%= album.name %> </h5>
<% orders.group_by { |order| order.song }.each do |song, orders| %>
<p>(<%= orders.count %>) <%= song.title %> </p>
<p><%= song.price %> </p>
<% end %>
<% end %>
<% end %>
<% end %>
Something like this perhaps? (in your controller)
#orders = Orders.where('created_at < ? and created_at > ?', Time.now.end_of_month, Time.now.beginning_of_month)

Conditional statement in "each...do" in Ruby?

In my erb file, I have the following code in the body tag:
<% #tasks.each do |task| %>
<%= task.name %>
<% end %>
This is working, but I only want to display task.name if task.otherAttribute does not equal -1.
For some reason, I can't figure out how to do this! Any tips much would be much appreciated.
Thank you in advance.
Try this:
<% #tasks.each do |task| %>
<%= task.name if task.otherAttribute != 1 %>
<% end %>
Or:
<% #tasks.each do |task| %>
<%= task.name unless task.otherAttribute == 1 %>
<% end %>
I will provide some more options for future reference:
<% #tasks.each do |task| %>
<% if task.otherAttribute != 1 %>
<%= task.name %>
<% end %>
<% end %>
<% #tasks.each do |task| %>
<%= task.otherAttribute == 1 ? '' : task.name %>
<% end %>
Good luck!
I tend to use #select and #reject for this idiom, since that's basically what you're doing.
<%= #tasks.reject{|t| t.other_attribute == -1}.each do |task| %>
<%= task.name %>
<% end %>
These come from the Enumerable module, which most things with an #each method include.
You can put conditionals into your ERB code.
<%= task.name if task.otherAttribute != 1 %>
You can also perform more complex tasks with a more verbose syntax. It's not necessary in your case, but you could also do more traditional if/else blocks like so:
<% if task.otherAttribute != 1 %>
<%= task.name %>
<% end %>

Resources