How to pass variable between methods in Ruby? - ruby

Newbie ruby question.
I have a class
class ReportPage < Page
def badge_item(item_int)
case item_int
when 1..50 then #item= 1
when 50..100 then #item= 50
end
def check_completed_items_badge
badge_item(50)
puts #item
end
end
Sure enough, it puts nil. And here comes my question - how can I use #item variable inside of the other method of the class?
Thank you a lot!

The thing is you miss end keyword for your case. And there is another problem in this program. when(1..50) contain all cases between 1 to 50, and when(50..100) covers 50 to 100, this will lead confusion cause badge_item(50) will go into the first line, set #item to 1 and quit the case ... end block. So at the end it will print 1 on the screen.
To make your intent more clearly, you should use
def badge_item(item_int)
#item = case item_int
when 1..49 then 1 #two dots, cover 1 to 49
when 50..100 then 50
end
end
OR
def badge_item(item_int)
#item = case
when 1...50 then 1 #three dots between, cover 1 to 49
when 50..100 then 50
end
end

The problem is not with the variable assignment but instead with your usage of the case/when syntax. When you give case an argument it uses exact matching to compare the different scenarios. A better way in your case would probably be to use something like this instead:
def badge_item(item_int)
case
when (1..50).include?(item_int) then #item = 1
when (50..100).include?(item_int) then #item = 50
end
end
Edit
Corrections from Tumtu:
case uses === method to compare values. i.e.: (1..50) === 50

The correct approach in your case is to use the OOP:
class ReportPage < Page
class BadgeItem
def initialize( int )
fail "Badge item integer should be in the range 0..99!" unless 0..99 === int
#int = int
end
def classify
case #int
when 0...50 then 1
when 50..99 then 50
end
end
end
def check_completed_items_badge
badge_item = BadgeItem.new 50
puts badge_item.classify
end
end

I know an answer has already been accepted but I wanted to present a more general answer to the question about how instance variables get tossed around.
Straight from ruby-lang you get the following:
Ruby needs no variable declarations. It uses simple naming conventions
to denote the scope of variables.
var could be a local variable.
#var is an instance variable.
$var is a global variable.
These sigils enhance readability by allowing the programmer to easily
identify the roles of each variable. It also becomes unnecessary to
use a tiresome self. prepended to every instance member
It is important to understand the distinction between these different types of variables. To do this I whipped up a small example that should help give anyone with navigates here a good starting point on how these behave.
class Scoping
#unreachable = "but found it. trololol"
$instance = "and found it. swag swag"
def initialize( int )
trial = #unreachable ? #unreachable : " but failed to find."
puts "reaching for the unreachable . . ." + trial
#int = int
end
end
puts $instance
Scoping.new 10
trial = #int ? #int : " but failed to find."
puts "Reaching for instance from class . . ." + trial
trial = $instance ? $instance : " but failed to find."
puts "Reaching for global variable . . ." + trial

Related

Not displaying it's corresponding values with it's key for Hash

Ok i am not here to ask for an answer. But to be honest i am not really good in class variable. So i would appreciate you can guide me along with this piece of code.
I have read on class variable at those docs. I some what kind of understand it. But it comes to applying it for my own use. I would get confused.
class Square
##sqArray = {}
#attr_accessor :length
def initialize
if defined?(##length)
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
else
randno = "%s" % [rand(20)]
##length = randno.to_i
##sqArray = ##length
end
end
def Area
##area = ##length * ##length
return ##area
##sqArray[##length.to_sym] = ##area
puts ##sqArray
end
end
s1 = Square.new
puts s1.Area
Let me explain this piece of code. Basically every time i create a Square object it would go to initialize method. A random number will be generated and pass it to ##length, and ##length will be assigned to hash ##sqArray as it's key. But now the problem is when i create a new object s1. When i want to display the Area i want to test out to print the hash ##sqArray with it's length as it's key and area as it's value. But now the problem is only returning it's area only. e.g 114 only.
suppose to be e.g [ 24 => 114]
When defining the object's property (i.e. it's length), the correct approach is to use an instance variable, not a class variable. This is because (in your particular example), length is an attribute of a specific square and not something that applies to all squares. Your code should look something like this:
class Square
def initialize(length = rand(20))
#length = length
end
def area
#length * #length
end
end
s1 = Square.new
puts s1.area
Now, I am a little unclear what exactly you aim to achieve by use of that class variable ##sqArray - but for example, you could use this store a list of all defined Squares:
class Square
##squares_list = []
def self.all_known
##squares_list
end
def initialize(length = rand(20))
#length = length
##squares_list << self
end
def area
#length * #length
end
end
This would allow you to write code like:
s1 = Square.new #=> #<Square:0x0000000132dbc8 #length=9>
s2 = Square.new(20) #=> #<Square:0x000000012a1038 #length=20>
s1.area #=> 81
s2.area #=> 400
Square.all_known #=> [#<Square:0x0000000132dbc8 #length=9>, #<Square:0x000000012a1038 #length=20>]
Class variables have some odd behaviour and limited use cases however; I would generally advise that you avoid them when starting out learning Ruby. Have a read through a ruby style guide to see some common conventions regarding best practice - including variable/method naming (use snake_case not camelCase or PascalCase), whitespace, etc.

Passing variables/parameters from one Ruby class to a loaded Ruby program

The below combined programs should ask for a number remove the first digit (lets call this new number x) and then compute x % 7. Ex: (1121546 % 7) = 5
This all appears to be working except that the number entered in will always compute to 0. modulo_7.rb works by itself and will print the correct outcome when passed a parameter.
The question is am I not passing the variables/ parameters properly or is there something else that is getting in the way?
class Number_check_interface
def get_cert_number
print "You are about to check receive the check number for a policy/cert id."
#cert_id = nil
until #cert_id.is_a?(Fixnum) do
puts " Enter the policy/cert ID. "
begin
#cert_id = Integer(gets)
rescue ArgumentError
end
end
end
end
class Run_number_check_interface
def run
load 'modulo_7.rb'
n = Number_check_interface.new
n.get_cert_number
checking_policy_number = Get_policy_check_digit.new(#cert_id)
checking_policy_number.create_check_digit
end
end
run = Run_number_check_interface.new
run.run
modulo_7.rb
This program removes the first digit (index 0) of a 7 digit number and returns the difference 7%18 is 4 since 4 is remainder of how many times 7 can fit into 18.
class Get_policy_check_digit
def initialize(cert_id)
#instance variable
#cert = cert_id
end
def create_check_digit
cert_id_6 = #cert.to_s
cert_id_6.slice!(0)
puts cert_id_6
check_digit = cert_id_6.to_i % 7
puts "Your check digit is #{check_digit}"
end
end
# n = Get_policy_check_digit.new(1121546) When uncommented this will create a new instance
# of Get_policy_check_digit with the parameter 1121546
# n.create_check_digit This will run the method create_check_digit with the
# parameter 1121546
Instance variables are scoped to an individual instance of a class. So when you say #cert_id = Integer(gets) inside Number_check_interface, you are only setting #cert_id for that particular instance of Number_check_interface. Then, when you later write Get_policy_check_digit.new(#cert_id) inside Run_number_check_interface, you are referring to an entirely different #cert_id, one which is specific to that particular instance of Run_number_check_interface, and not to the Number_check_interface you stored in n earlier.
The simple solution is to return #cert_id from Number_check_interface#get_cert_number, and then pass the returned value to Get_policy_check_digit.new:
class Number_check_interface
def get_cert_number
print "You are about to check receive the check number for a policy/cert id."
#cert_id = nil # This is an instance variable. It's only accessible
# from inside this instance of `Number_check_interface`
until #cert_id.is_a?(Fixnum) do
puts " Enter the policy/cert ID. "
begin
#cert_id = Integer(gets)
rescue ArgumentError
end
end
return #cert_id # Return the contents of #cert_id
end
end
class Run_number_check_interface
def run
load 'modulo_7.rb'
n = Number_check_interface.new
cert_number = n.get_cert_number # Here we call `get_cert_number` and
# set `cert_number` to it's return
# value.
# Notice how we use `cert_number` here:
checking_policy_number = Get_policy_check_digit.new(cert_number)
checking_policy_number.create_check_digit
end
end
Other tips:
Convention in Ruby is to name classes with CamelCase.
Require dependencies at the top of your files, not in the middle of method calls.
Unless you have a very good reason not to, use require, not load
You might want to think a bit harder about what purpose these classes serve, and what behavior they are intending to encapsulate. The API seems a bit awkward to me right now. Remember, tell, don't ask.
Why are these separate classes? The design here seems strange, is this ported from another language? It's more complicated than it needs to be. Without changing your structure, here's what's wrong:
In Run_number_check_interface you are reading #cert_id, it doesn't have an instance variable named that, but Number_check_interface does. Just return it from get_cert_number:
class Number_check_interface
def get_cert_number
print "You are about to check receive the check number for a policy/cert id."
cert_id = nil
until cert_id.is_a?(Fixnum) do
puts " Enter the policy/cert ID. "
begin
cert_id = Integer(gets)
rescue ArgumentError
end
end
cert_id # <-- returing the value here
end
end
class Run_number_check_interface
def run
load 'modulo_7.rb'
n = Number_check_interface.new
cert_id = n.get_cert_number # <-- saving here
checking_policy_number = Get_policy_check_digit.new(cert_id) # <-- no longer an ivar
checking_policy_number.create_check_digit
end
end
run = Run_number_check_interface.new
run.run

calling an array defined in one method in another method

am learning ruby and i had come across this particular issue.
I have method which reads the user input data into an array and i have another method which displays the values in the same array to the user with some processing.
However this doesnt seem to be the correct way as the system always throws a
Arraypass.rb:23:in <main>': undefined local variable or methodnames' for main:Object (NameError)
Appreciate if someone can show a way forward in this,
for example:
class School
def askdetails
print "How many students are there"
n=(gets.chomp.to_i - 1)
print "Enter names one by one"
names=Array.new(n)
for i in (0..n)
names[i]=gets.chomp
end
return names,n
end
def showdetails(names,n)
for i in (0..n)
puts names[i]
end
end
end
stud=School.new
stud.askdetails
stud.showdetails(names,n)
Write the code as
#!/usr/bin/env ruby
class School
def askdetails
print "How many students are there"
n = gets.chomp.to_i - 1
print "Enter names one by one"
names = Array.new(n)
for i in (0..n)
names[i]=gets.chomp
end
return names,n
end
def showdetails(names,n)
for i in (0..n)
puts names[i]
end
end
end
stud = School.new
names, n = stud.askdetails
stud.showdetails(names,n)
The thing, you missed is #askdetails methods returning an Array, which you didn't assign any where before using those.
Read Array Decomposition, this is what I did here :
names, n = stud.askdetails
here is your answer:
names,n = stud.askdetails
stud.showdetails(names,n)

Dynamic methods using define_method and eval

I've put together two sample classes implemented in a couple of different ways which pretty well mirrors what I want to do in my Rails model. My concern is that I don't know what, if any are the concerns of using either method. And I've only found posts which explain how to implement them or a general warning to avoid/ be careful when using them. What I have not found is a clear explanation of how to accomplish this safely, and what I'm being careful of or why I should avoid this pattern.
class X
attr_accessor :yn_sc, :um_sc
def initialize
#yn_sc = 0
#um_sc = 0
end
types = %w(yn um)
types.each do |t|
define_method("#{t}_add") do |val|
val = ActiveRecord::Base.send(:sanitize_sql_array, ["%s", val])
eval("##{t}_sc += #{val}")
end
end
end
class X
attr_accessor :yn_sc, :um_sc
def initialize
#yn_sc = 0
#um_sc = 0
end
types = %w(yn um)
types.each do |t|
# eval <<-EVAL also works
self.class_eval <<-EVAL
def #{t}_add(val)
##{t}_sc += val
end
EVAL
end
end
x = X.new
x.yn_add(1) #=> x.yn_sc == 1 for both
Well, your code looks realy safe. But imagine a code based on user input. It might be look something like
puts 'Give me an order, sir!'
order = gets.chomp
eval(order)
What will happen if our captain will go wild and order us to 'rm -rf ~/'? Sad things for sure!
So take a little lesson. eval is not safe because it evaluates every string it receives.
But there's another reason not to use eval. Sometimes it evaluates slower than alternatives. Look here if interested.

Adding conversion method to Numeric causes SystemStackError

I'm attempting to add conversion methods to the Numeric class but when I run the following lines of code I get a SystemStackError
puts 5.dollars.in(:euros) # => 6.5
puts 1.dollar.in(:yen)
Here is my Numeric class
class Numeric
##conversion_hash = {:dollar => {:yen => 0.013, :euros => 1.292, :rupees => 0.019}}
def method_missing(method_id)
name = method_id.to_s
if name =~ /^dollar|yen|euros|rupee|$/
self.send(name + 's')
else
super # pass the buck to superclass
end
end
def dollars()
puts "Called Dollars method"
#current_currency = :dollar
return self
end
def in(key)
if ##conversion_hash.has_key?(#current_currency)
puts "Current currency: #{#current_currency}"
conversion_rate = ##conversion_hash[#current_currency]
puts "Current conversion rate: #{conversion_rate}"
if conversion_rate.has_key?(key)
puts "we have that key"
puts"What am I? #{self}"
rate = conversion_rate[key]
puts "Rate to multiply by #{rate}"
return self.to_int * conversion_rate[key]
end
end
end
end
Any help is greatly appreciated.
You're getting infinite recursion in your method_missing because your regex isn't quite right. Try changing the line:
if name =~ /^dollar|yen|euros|rupee|$/
to:
if name =~ /^dollar|yen|euros|rupee$/
That extra | is causing anything to match the regex, so any other method is recursing with a continually extending suffix of s.
In this case it looks like puts seems to be trying to call to_ary when it's trying to determine the type its argument. I'm not exactly sure why it's not just using respond_to? though - it's deep in the C internals so I don't really know what's happening.
Your solution overcomplicated.
- You don't need to modify method_missing. Armando version works fine.
- You should simple dollar definition to hash plus
- find the way to call method_missing again from method in(this is your homework).
Working solution have only 1 line of code + 2 lines def surronding.

Resources