Accessing an object's instance variable from an array - arrays

Teaching myself Ruby so please bear with me. If I create an object with several defining attributes and push this object into an array how do I access one of those attributes in another method to use it in a control flow scheme? I'm making a banking ATM program for fun. My code is below...
class Bank
class AccountMaker
attr_accessor :account_number, :name, :balance, :pin
def initialize(account_number, name, balance, pin)
#account_number = account_number
#name = name
#balance = balance
#pin = pin
end
end
def initialize
#accounts = []
end
def add_account(account_number, name, balance, pin)
account = AccountMaker.new(account_number, name, balance, pin)
#accounts << account
end
def login_screen(accounts)
def account_number_login(accounts)
puts "Please enter your 7 digit account number."
account_number_input = gets.chomp
puts accounts.instance_variable_get(:account_number)
if (/^\d{7}$/ === account_number_input) and (account_number_input === (what should go here) )
thank_you_msg()
pin_login(account_number_input)
else
error_msg()
account_number_login()
end
end
I have more code after this but its not pertinent to the question. Essentially I want to extract from the accounts array :account_number and use it in the if statement within the Login_screen function to see if the account actually exists. Any and all help would be appreciated.

accounts is an array. So you have to access one of its elements' account_number instance variable. For example the first element's:
# accounts[0] would return an instance of `AccountMaker `
accounts[0].instance_variable_get(:account_number)
Also, you don't need to use instance_variable_get, since you already declared it as accessor. So, you can just call account_number method on it.
accounts[0].account_number

Related

search array in ruby and return results as collection of objects?

I am a noob in ruby, so take this with a grain of salt.
I have a Person which I've posted below. I am supposed to search the people array using last name, and return the results in an array so I can print it using a put statement such as puts Person.search("Smith")
here's the class that I am working on:
class Person
attr_accessor :first_name,:last_name
##people = []
def initialize(x,y)#should take 2 parameters for first_name and last_name
#first_name = x
#last_name = y
##people << "#{#x} #{#y}"
end
#my problem is that the function is returning nill.
def self.search(last_name)
#results = []
##people.each do |variable|
if variable.include? last_name
#results << variable
end
end
#results
end
def to_s
puts "#{#first_name} #{#last_name}"
#return a formatted string as `first_name(space)last_name`
end
end
p1 = Person.new("John", "Smith")
p2 = Person.new("John", "Doe")
p3 = Person.new("Jane", "Smith")
p4 = Person.new("Cool", "Dude")
I declared results as an empty array outside the block so I can alter its values and add items to it inside the block and maintain the changes outside the scope of the block. but for some reason the function doesn't seem to work. how can I debug the function so I can pinpoint the problem?
There is an error in your code. You need to use
##people << "#{x} #{y}"
x and y are local variables, and you should not prefix # to it as #x and #y will only work for instance variables

Trouble adding elements to a table (array in Lua)

I am attempting to create a table to serve as a small database for users:
users = {}
function create_new_user()
print("Enter a unique user name (up to 12 letters): ")
local name = io.read()
if #name > 12 then
print ("That name is too long.")
return create_new_user()
elseif users[name] then
print ("That name is already in use.")
return create_new_user()
else
table.insert(users, 1, name)
print("Your new user name is: ", users[name])
end
end
I understood from the manual that the line
table.insert(users, 1, name)
would insert the string value of name as an element of the users array. This is not the case-- whenever I run the script I get the following output:
Your new user name is: nil
You insert the element into the table, but you are trying to retrieve the value indexed by the value of name, which is not what you stored (you are using users[name] instead of users[1]). You can probably do something like this:
table.insert(users, name)
print("Your new user name is: ", name)
Note that table.insert(users, 1, name) may not do what you expect as this will prepend elements to the table. If you insert "abc" and "def" this way, then the users table will include elements {"def", "abc"} (in this particular order). To retrieve the last inserted element you can use users[1].
If you want to store values in a different order, you need to use table.insert(users, name), which will append elements to the table. To retrieve the last element you can use users[#users].
If you always want to store the added element in the first position in the table, then you can simply use users[1] = name.
Here you index the user table with a string (the name):
elseif users[name] then
You do the same here:
print("Your new user name is: ", users[name])
But you store the name with a numerical index:
table.insert(users, 1, name)
What you want instead of that line is:
users[name] = name
Or this (which would require changing the line that follows):
users[name] = true
The idea is you're only really using the keys, to create a lookup table.

How to end this loop / logically end pseudocode

I'm writing a pseudocode program to read customer record, determine their account type, and then output their name and amount owed at the end. I wrote a program (almost done) I'm just not sure how to loop it until there are no more records left. Can you guys help me out? My output at the end is just supposed to be Customer Name and Amount Owed. Thanks.
read_customer_record
get num_of_records
get customer_name
get account_type
get num_basic_channels
get num_premium_channels
calculate_rate (calcR)
calculate_totals (calcT)
output(outp)
END
Calculate_rate (calcR)
IF account_type = personal
Basic_rateP = $5
Pre_rateP = $10
ELSE
Basic_rateB = $7.50
Pre_rateB = $12.50
END
Calculate_totals (calcT, calcR)
IF account_type = personal
total = (num_basic_channels * Basic_rateP) + (num_premium_channels * Pre_rateP)
ELSE
total = (num_basic_channels * Basic_rateB) + (num_premium_channels * Pre_rateB)
END
output (
You can do like this:
>>> Print Customer Name and Amount owed , till Customer Name is not equal to Null.
If I write the code in C++, it will look something like this.
for(i=0;CustomerName[i]!=NULL;i++) //Loop till there is some Customer Name exists
{
cout<<CustomerName[i]<<"\t"<<AmountOwed[i]; //Printing out the Data
}
It will stop when there will be no Customer Remaining and when there is not Customer , obviously there is no Amount Owed.
I think you just need to wrap a WHILE statement around your main routine. The way to do that depends on where you are reading the customer record from.
Case 1: In many contexts you can only tell that you have finished processing the input stream by attempting to read past the end. In that case your pseudocode might look like:
read_customer_record
while the previous read succeeded
get num_of_records
get customer_name
get account_type
get num_basic_channels
get num_premium_channels
calculate_rate (calcR)
calculate_totals (calcT)
output(outp)
read_customer_record
end
Case 2: In other contexts you can tell before a read whether there is something left to read from the input stream. In that case your pseudocode might look like:
while there are more customer records to process
read_customer_record
get num_of_records
get customer_name
get account_type
get num_basic_channels
get num_premium_channels
calculate_rate (calcR)
calculate_totals (calcT)
output(outp)
end

bad use of variables in db.query

I'm trying to develop a blog using webpy.
def getThread(self,num):
myvar = dict(numero=num)
print myvar
que = self.datab.select('contenidos',vars=myvar,what='contentTitle,content,update',where="category LIKE %%s%" %numero)
return que
I've used some of the tips you answer in this web but I only get a
<type 'exceptions.NameError'> at /
global name 'numero' is not defined
Python C:\xampp\htdocs\webpy\functions.py in getThread, line 42
Web GET http://:8080/
...
I'm trying to make a selection of some categorized posts. There is a table with category name and id. There is a column in the content table which takes a string which will be formatted '1,2,3,5'.
Then the way I think I can select the correct entries with the LIKE statement and some %something% magic. But I have this problem.
I call the code from the .py file which builds the web, the import statement works properly
getThread is defined inside this class:
class categoria(object):
def __init__(self,datab,nombre):
self.nombre = nombre
self.datab = datab
self.n = str(self.getCat()) #making the integer to be a string
self.thread = self.getThread(self.n)
return self.thread
def getCat(self):
'''
returns the id of the categorie (integer)
'''
return self.datab.select('categorias',what='catId', where='catName = %r' %(self.nombre), limit=1)
Please check the correct syntax for db.select (http://webpy.org/cookbook/select), you should not format query with "%" because it makes code vulnerable to sql injections. Instead, put vars in dict and refer to them with $ in your query.
myvars = dict(category=1)
db.select('contenidos', what='contentTitle,content,`update`', where="category LIKE '%'+$category+'%'", vars=myvars)
Will produce this query:
SELECT contentTitle,content,`update` FROM contenidos WHERE category LIKE '%'+1+'%'
Note that I backquoted update because it is reserved word in SQL.

Between query equivalent on App Engine datastore?

I have a model containing ranges of IP addresses, similar to this:
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
On a SQL database, I would be able to find rows which contained an IP in a certain range like this:
SELECT * FROM Country WHERE ipnum BETWEEN begin_ipnum AND end_ipnum
or this:
SELECT * FROM Country WHERE begin_ipnum < ipnum AND end_ipnum > ipnum
Sadly, GQL only allows inequality filters on one property, and doesn't support the BETWEEN syntax. How can I work around this and construct a query equivalent to these on App Engine?
Also, can a ListProperty be 'live' or does it have to be computed when the record is created?
question updated with a first stab at a solution:
So based on David's answer below and articles such as these:
http://appengine-cookbook.appspot.com/recipe/custom-model-properties-are-cute/
I'm trying to add a custom field to my model like so:
class IpRangeProperty(db.Property):
def __init__(self, begin=None, end=None, **kwargs):
if not isinstance(begin, db.IntegerProperty) or not isinstance(end, db.IntegerProperty):
raise TypeError('Begin and End must be Integers.')
self.begin = begin
self.end = end
super(IpRangeProperty, self).__init__(self.begin, self.end, **kwargs)
def get_value_for_datastore(self, model_instance):
begin = self.begin.get_value_for_datastore(model_instance)
end = self.end.get_value_for_datastore(model_instance)
if begin is not None and end is not None:
return range(begin, end)
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
ip_range = IpRangeProperty(begin=begin_ipnum, end=end_ipnum)
The thinking is that after i add the custom property i can just import my dataset as is and then run queries on based on the ListProperty like so:
q = Country.gql('WHERE ip_range = :1', my_num_ipaddress)
When i try to insert new Country objects this fails though, complaning about not being able to create the name:
...
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/db/__init__.py", line 619, in _attr_name
return '_' + self.name
TypeError: cannot concatenate 'str' and 'IntegerProperty' objects
I tried defining an attr_name method for the new property or just setting self.name but that does not seem to help. Hopelessly stuck or heading in the right direction?
Short answer: Between queries aren't really supported at the moment. However, if you know a priori that your range is going to be relatively small, then you can fake it: just store a list on the entity with every number in the range. Then you can use a simple equality filter to get entities whose ranges contain a particular value. Obviously this won't work if your range is large. But here's how it would work:
class M(db.Model):
r = db.ListProperty(int)
# create an instance of M which has a range from `begin` to `end` (inclusive)
M(r=range(begin, end+1)).put()
# query to find instances of M which contain a value `v`
q = M.gql('WHERE r = :1', v)
The better solution (eventually - for now the following only works on the development server due to a bug (see issue 798). In theory, you can work around the limitations you mentioned and perform a range query by taking advantage of how db.ListProperty is queried. The idea is to store both the start and end of your range in a list (in your case, integers representing IP addresses). Then to get entities whose ranges contain some value v (i.e., between the two values in your list), you simply perform a query with two inequality filters on the list - one to ensure that v is at least as big as the smallest element in the list, and one to ensure that v is at least as small as the biggest element in the list.
Here's a simple example of how to implement this technique:
class M(db.Model):
r = db.ListProperty(int)
# create an instance of M which has a rnage from `begin` to `end` (inclusive)
M(r=[begin, end]).put()
# query to find instances of M which contain a value `v`
q = M.gql('WHERE r >= :1 AND r <= :1', v)
My solution doesn't follow the pattern you have requested, but I think it would work well on app engine. I'm using a list of strings of CIDR ranges to define the IP blocks instead of specific begin and end numbers.
from google.appengine.ext import db
class Country(db.Model):
subnets = db.StringListProperty()
country_code = db.StringProperty()
c = Country()
c.subnets = ['1.2.3.0/24', '1.2.0.0/16', '1.3.4.0/24']
c.country_code = 'US'
c.put()
c = Country()
c.subnets = ['2.2.3.0/24', '2.2.0.0/16', '2.3.4.0/24']
c.country_code = 'CA'
c.put()
# Search for 1.2.4.5 starting with most specific block and then expanding until found
result = Country.all().filter('subnets =', '1.2.4.5/32').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/31').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/30').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.0/29').fetch(1)
# ... repeat until found
# optimize by starting with the largest routing prefix actually found in your data (probably not 32)

Resources