Syntax Error: unexpected $end when using if/else if? - ruby-on-rails

I'm getting an error from one of my controller classes and I can't figure out why. The error is:
SyntaxError in TermsController#show, syntax error, unexpected $end, expecting keyword_end
Here is terms_controller.rb:
class TermsController < ApplicationController
def show
#term = Term.find(params[:id])
if #term.id == 1
#title = "Fall"
else if #term.id == 2
#title = "Winter"
else if #term.id == 3
#title = "Spring"
else if #term.id == 4
#title = "Summer"
end
end
end
My show page currently just consists of:
<h1> <%= #title %> </h1>
It's probably something small that I'm just missing - thanks for your help!

The issue that there are not enough end keywords and it found $end (the token representing the end of the file) before it could find what it was looking for -- another end. (The parser token for the end keyword is either "keyword_end" or "Kend", depending upon ruby version.)
Each if expression requires a matching end keyword.
To get around this, use elsif instead of else if. It is part of the same if construct and does not require a matching end (only the if requires the matching end).
if x == 1
"1"
elsif x == 2
"2"
else
"else"
end
Another option is case which works well if all branches check the same conditional operand (x in this case):
case x
when 1 then "1"
when 2 then "2"
else "else"
end
If you do want to use else if (remember, each if starts a new if conditional construct) then make sure close each block that a if opens. I have indented the code to show this point better.
if x == 1
"1"
else
if x == 2
"2"
else
"else"
end
end
Happy coding.
For the pedantic: there is also another form of if, which is expr if cond, which doesn't have a matching end as part of the syntax and the rules talked about above do not apply to it.
In addition, if and case are just expressions in Ruby, so it might be more idiomatically written like this
#term = Term.find(params[:id])
#title = case #term.id
when 1 then "Fall"
when 2 then "Winter"
when 3 then "Spring"
when 4 then "Summer"
else "Invalid Term"
end
The if/elsif/end syntax can be used in the same way, but using case avoids the repeated mentioning of #term.id. Another option is to use a Hash to perform this sort of simple mapping -- or the mapping can be encapsulated in a separate method -- but that is covered elsewhere ;-)

Instead of else if use elsif.

Why not just do this:
class TermsController < ApplicationController
##seasons = { 1 => "Fall", 2 => "Winter", 3 => "Spring", 4 => "Summer"}
def show
#term = Term.find(params[:id])
#title = ##seasons[params[:id]] || "Invalid Season"
end
end

Related

Method definition gets 'syntax error, unexpected '=', expecting keyword_then or ';' or '\n'

I am trying to define a method in a model and on the last elsif line i get a syntax error stating "/app/models/purchase.rb:23: syntax error, unexpected '=', expecting ')' elsif (self.invoices.sum(:current_balance) = 0 ^ ".
def payment_status
if self.invoices.blank?
self.payment_status = "No Invoices"
else
if self.invoices.sum(:current_balance) > 0
self.payment_status = "Open"
elsif self.invoices.sum(:current_balance) < 0
self.payment_status = "Overpaid"
elsif self.invoices.sum(:current_balance) = 0
self.payment_status = "Paid"
end
end
end
I certainly do mean to use the equals sign there, so I I'm lost as to what the problem is. Any ideas?
elsif self.invoices.sum(:current_balance) = 0
^---
that's an assignment operation. you want an equality test, which is ==.
Let's clean up the code a little bit followed by an explanation:
def payment_status
if invoices.blank?
"No Invoices"
elsif invoices.sum(:current_balance) > 0
"Open"
elsif invoices.sum(:current_balance) < 0
"Overpaid"
else
"Paid"
end
end
You don't need to use self in front of every function call unless there is a local variable with the same name and you need to differentiate them.
An if statement automatically returns the value that it "selects" based on the condition and functions automatically return their last statement.
You don't need to set payment_status each time. It will automatically be the return value (the last value of the function)
If you want to run this code only once, store the value in a variable, and use the function multiple times, you can memoize this with the following code:
def payment_status
#payment_status ||= begin
if invoices.blank?
"No Invoices"
elsif invoices.sum(:current_balance) > 0
"Open"
elsif invoices.sum(:current_balance) < 0
"Overpaid"
else
"Paid"
end
end
For more examples on how/when to memoize click here or here

Few line of codes are geting escaping at active admin custom page page_action

While working with Active Admin-> Custom Page -> page_action, I am facing difficulty in following code block pointed in the code. I was trying to test code reachablity by exception. I don't understand why won't I get exception if I place exception in position 2 in the code?
page_action :add_event, method: :post do
blogit_posts=params["featured_post"]["blog_posts_attributes"].values.map { |s|
{
:blogit_post_id=>s["blogit_post_id"],
:id=> s["id"] ? s["id"] : s["i/nd"],
:priority=>s["priority"],
:_destroy=>s["_destroy"]
}
}
blogit_posts.each do |blog_hash|
#raise "unknown" <-- 1. if i put here, I get exception for it
if blog_hash[:id]
b=BlogPost.find_by_id(blog_hash[:id].to_i)
else if blog_hash[:blogit_post_id]
b=BlogPost.find_by_blogit_post_id(blog_hash[:blogit_post_id].to_i)
end
#raise "unknown" <-- 2. if i put here, I **DO NOT** get exception for it
if blog_hash[:_destroy] && blog_hash[:_destroy]=="1"
b.is_featured=false # <--- trying to fix this code block
else
b.is_featured=true
end
b.priority =blog_hash[:priority].to_i
b.save
end
end
redirect_to admin_featuredpost_path, notice: "Featurd post updated "
end
If you were trying to format the code appropriately, you would’ve immediately found the error:
if blog_hash[:id]
b=BlogPost.find_by_id(blog_hash[:id].to_i)
else
if blog_hash[:blogit_post_id]
b=BlogPost.find_by_blogit_post_id(blog_hash[:blogit_post_id].to_i)
end
???
That said, you have opened if, that is closed 15 lines below. One should either use elsif for spaghetti ifs or properly close nesting ifs.
You probably are having trouble with this block:
if blog_hash[:id]
b=BlogPost.find_by_id(blog_hash[:id].to_i)
else if blog_hash[:blogit_post_id]
b=BlogPost.find_by_blogit_post_id(blog_hash[:blogit_post_id].to_i)
end
I believe what you meant was to write this:
if blog_hash[:id]
b=BlogPost.find_by_id(blog_hash[:id].to_i)
elsif blog_hash[:blogit_post_id]
b=BlogPost.find_by_blogit_post_id(blog_hash[:blogit_post_id].to_i)
end
please notice the elsif

I don't understand a end keyword usage

I'm getting pretty confused with the keyword end. For example, in the following code, I'm tried to define a my own helper method but I got too many SyntaxErrors because I missed some ends. I added some and it's works but... I don't understand why I have to put them, which block they close.
I marked them with multiple questions marks.
Thanks guys!
module ApplicationHelper
def notice_color(notice)
if notice
type = type_notice(notice)
div_tag_head = "<div class=\"alert alert-#{type} alert-dismissable\">"
cross_button = "<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">×</button>"
notice_tag = div_tag_head + cross_button + notice + "</div>"
notice_tag.to_s.html_safe
end # If's end.
end # Def's end.
def type_notice(notice)
downcased = notice.downcase
if downcased.include? 'error' or downcased.include? 'invalid'
return'danger'
else if downcased.include? 'done' or downcased.include? 'success'
return 'success'
else if downcased.include? 'hey'
return 'warning'
else
return 'info'
end # If's end.
end #Def's end
end # ?????? <------ First
private :type_notice
end # ??????? <------ Second
end # Module's end
Your problem is in the if block. Ruby syntax is elsif not else if:
if downcased.include? 'error' or downcased.include? 'invalid'
return'danger'
elsif downcased.include? 'done' or downcased.include? 'success'
return 'success'
elsif downcased.include? 'hey'
return 'warning'
else
return 'info'
end
Your two else if lines are actually starting two new if statements, hence why you needed a couple of extra ends.
As Graeme stated, be careful to use elsif instead of else if.
However, indentation may also have played a role in the confusion. If you're using Sublime Text, check the Preferences.sublime-settings file and make sure there's a line that says "tab_size": 2 or "tab_size": n (n being any number you're comfortable with, though 2 is de facto industry standard). Be careful to add a comma after the code if it's in the middle of the hash.
To access the preferences file, in Sublime Text, press Shift + Command + p (on an Apple) and type 'user,' or find the Preferences menu option and select 'Settings - User.'

return result back to view

i am trying to keep all my logic out of views, and have come up with the following piece of code, the though im having is that it isnt returning the actual score value, it just returns if it was a Win, lost or tie
def find_result(schedule)
return "not required" if schedule.event != '1' or schedule.time >= Time.now
if schedule.for.nil? or schedule.against.nil?
"Not Entered"
else
tie = '<b>T</b> '
tie << schedule.for.to_i
tie << ' - '
tie << schedule.against.to_i
win = '<b>W</b> '
win << schedule.for.to_i
win << ' - '
win << schedule.against.to_i
return raw tie if schedule.for.to_i == schedule.against.to_i
schedule.for.to_i > schedule.against.to_i ? (raw win) : "Lost"
end
end
Don't use << with an integer. See the docs:
http://www.ruby-doc.org/core-1.9.3/String.html#method-i-3C-3C
It's probably turning your win/loss numbers into characters that aren't showing up in the HTML.
Use a formatter or something, or perhaps just to_s rather than to_i when appending the numbers.
Example using string format (untested):
def find_result(schedule)
return "not required" if schedule.event != '1' or schedule.time >= Time.now
if schedule.for.nil? or schedule.against.nil?
"Not Entered"
elsif schedule.for.to_i < schedule.against.to_i
"Lost"
else
raw "<b>%s</b> %d - %d" % [
schedule.for.to_i == schedule.against.to_i ? 'T' : 'W',
schedule.against.to_i,
schedule.for.to_i
]
end
Edit: Refactor
Keeping logic out of the views is good, but it would be even more appropriate to
move some of this to the model, namely the result of the schedule (not entered,
win, loss, tie)
In the example I'll make a simple inner class which encapsulates that logic, which
the Schedule makes use of to know its own result. You could do this any number of ways
though (e.g. a module versus a class, or methods directly on Schedule)
I'll then demonstrate how you might use the new schedule in your helper using the logic provided, or simply querying for the result itself and using it as a key for a translation lookup (I18n).
Note this is untested and a little bit pseudo-codey (I'm not using any I18n library in particular, just guessing at methods and translation formatting). But it should work with some tweaking, or at least give you an idea of another way of doing things.
class Schedule
# The schedule jus instantiates a result object when you ask for one.
# For convenience the result's to_s is it's value, e.g. "win"
def result
Result.new(self.for, self.against)
end
# delegate methods querying the result
delegate :win?, :loss?, :tie?, :not_entered?, :to => :result
class Result
Values = %(win loss tie not_entered)
Win = Values[0]
Loss = Values[1]
Tie = Values[2]
NotEntered = Values[3]
attr_reader :for, :against
def initialize(_for, against)
#for = _for
#against = against
end
def value
return NotEntered unless [#for, #against].all?
case v = #for - #against
when v.zero? then Tie
when v > 0 then Win
else Loss
end
end
alias :to_s :value
def not_entered?; self.value == NotEntered end
def win?; self.value == Win end
def loss?; self.value == Loss end
def tie?; self.value == Tie end
end
end
# then in your helper, something like
def find_result(schedule)
# you'd want to refactor this requirement part too
return "not required" if schedule.event != '1' or schedule.time >= Time.now
# Now you could do it essentially the way you had, with ifs or a
# case statement or what have you, but the logic for the result is kept
# where it belongs, on the class.
if schedule.not_entered?
"Not Entered"
elsif schedule.loss?
"Loss"
else
prefix = schedule.win? ? "W" : "T"
raw "<b>%s</b> %d - %d" % [prefix, schedule.for, schedule.against]
end
# OR you could use some kind of translation library using the `value`
# returned by the result. Something like:
key = ["schedule", schedule.outcome.value].join(".")
raw I18n.translate(key, {:for => schedule.for, :against => schedule.against})
end
# if you used the latter, it would lookup the translation in some other place,
# e.g. some config JSON, which might look like this (more or less, and
# depending on the lib you use):
{
"schedule": {
"win": "<b>W</b> {{for}} - {{against}}",
"tie": "<b>T</b> {{for}} - {{against}}",
"loss": "Loss",
"not_entered": "Not Entered"
}
}
# The translation has a few advantages. It would allow you to sub in other
# languages, but also, it conveniently keeps all of the app's text in one
# place, if you stick to using it.

Ruby on Rails: Execute Logic Based on Selected Menu

I have a class that I use to contain select menu options for property types. It works fine. However, I need to be able to verify the selection and perform specific logic based on the selected option. This needs to happen in my Ruby code and in JavaScript.
Here is the class in question:
class PropertyTypes
def self.[](id)
##types[id]
end
def self.options_for_select
##for_select
end
private
##types = {
1 => "Residential",
2 => "Commercial",
3 => "Land",
4 => "Multi-Family",
5 => "Retail",
6 => "Shopping Center",
7 => "Industrial",
8 => "Self Storage",
9 => "Office",
10 => "Hospitality"
}
##for_select = ##types.each_pair.map{|id, display_name| [display_name, id]}
end
What is the best way to verify the selection? I need to perform specific logic and display user interface elements based on each type of property type.
Since I am storing the id, I would be verifying that the id is a particular property type. Something like:
PropertyTypes.isResidential?(id)
Then this method would look like this:
def self.isResidential?(id)
##types[id] == "Residential"
end
But now I am duplicating the string "Residential".
For JavaScript, I assume I would make an ajax call back to the model to keep the verification code DRY, but this seems like over kill.
Do I need to manually create a verification method for each property type or can I use define_method?
This seems so basic yet I am confused and burned out on this problem.
Thanks
===
Here's my solution:
class << self
##types.values.each do |v|
# need to remove any spaces or hashes from the found property type
v = v.downcase().gsub(/\W+/, '')
define_method "is_#{v}?", do |i|
type_name = ##types[i]
return false if type_name == nil #in case a bogus index is passed in
type_name = type_name.downcase().gsub(/\W+/, '')
type_name == v
end
end
end
It sounds like you can benefit from some Ruby meta-programming. Try googling "ruby method_missing". You can probably do something quick & dirty along the lines of:
class PropertyTypes
def method_missing(meth, *args, &block)
if meth.to_s =~ /^is_(.+)\?$/
##types[args.first] == $1
else
super
end
end
end
On the ruby side you could also use something like this to define dynamically these methods:
class << self
##types.values.each do |v|
define_method "is_#{v}?", do |i|
##types[i] == v
end
end
end

Resources