Accessing Ruby class methods with io console - ruby

I'm creating a simple task manager in Ruby and am having trouble running a class method on an object that I've created. Here's my backend code:
class TM::Project
attr_accessor :name, :pid, :tasks
##projects = []
def initialize(name=nil)
#name = name
#pid = ##projects.size
#tasks = []
##projects << self
end
def add_task(priority, description)
task = TM::Task.new(priority, description)
#tasks << task
end
end
class TM::Task
attr_accessor :tid, :description, :priority, :status, :creation_date
##tasks = []
def initialize(priority, description)
#description = description
#priority = priority
#tid = ##tasks.size
##tasks << self
#status = "incomplete"
#creation_date = Time.now
end
end
And here's the code for the console:
require_relative 'lib/task-manager.rb'
class Task_io
##io_projects = []
def start
puts "Welcome to Task Manager. How may we assist you?\n\n"
puts "Available Commands: \n"
puts " create NAME - Create a new project with name=NAME"
puts " add PID PRIORITY DESC - Add a new task to project with id=PID"
puts 'Enter request>'
req = gets.chomp
input = req.split
if input[0] == 'create'
proj = TM::Project.new(input[1])
puts "#{proj.name} was created with ID=#{proj.pid}"
##io_projects << proj
self.start
elsif input[0] == 'add'
i = 0
proj_id = input[1].to_i
##io_projects[proj_id].add_task(input[2], input[3].to_s)
puts "#{input[3].to_s} was assigned TID #{##tasks[i + 1]}"
i += 1
self.start
else
puts "Invalid request, please try again"
self.start
end
end
end
I'm able to create projects, but when I try to add a task to the project I get the following error:
undefined method `add_task' for nil:NilClass
Am I accessing the object incorrectly?

Related

obj.method(argument)

I am looking at this code:
class Student
attr_accessor :first_name, :last_name, :age
def initialize(first, last, age)
#first_name = first
#last_name = last
#age = age
end
def birthday
#age += 1
end
end
class ViewStudent
def initialize(student)
#student = student
end
def do_something
puts "Student name: #{#student.first_name} #{#student.last_name}"
end
end
class UpdateStudent
def initialize(student)
#student = student
end
def do_something
puts "What is the student's first name?"
#student.first_name = gets.chomp
puts "What is the student's last name?"
#student.last_name = gets.chomp
puts "Updated student: #{#student.first_name} #{#student.last_name}"
end
end
choices = [ViewStudent, UpdateStudent]
student = Student.new("John", "Doe", 18)
puts "Select 1 to view student or 2 to update student."
selection = gets.chomp.to_i
obj = choices[selection - 1]
obj = obj.new(student)
obj.do_something
In the last five lines, I understand that selection = gets.chomp.to_i converts the selection options to integers, but how does that work in tandem with obj = choices[selection - 1]?
I'm also not sure what obj = obj.new(student) and obj.do_something do. It looks like a local variable is being set to create a new object with student as the argument. However, obj isn't a class or method to call on?
I can also gather that obj.do_something calls the methods defined for both ViewStudent and UpdateStudent given the selection.
I saw this, but it doesn't answer my question.
obj = choices[selection - 1] just select ViewStudent if 1 and UpdateStudent if 2 from your array by index (choices[0] or choices[1]).
Then you creating an instance of selected class (ViewStudent.new or UpdateStudent.new) and call do_something method on this instance, because this methos difined in both classes:
obj = choices[selection - 1] # obj is ViewStudent or UpdateStudent class
obj = obj.new(student) # obj is ViewStudent or UpdateStudent instance
obj.do_something # call `do something` method on instance

Why do i never get to on_connect in ShadyHttpClient

So witness and observe the following code, my questions is why do i never make it to the on_connect after starting the cool.io loop in send_to_server, the l.run should fire off the request as per the documented example on the github, and how the code handles incoming connections in module Server #socket.attach(l)
l.run
which does work and accepts the incoming data and sends it to my parser, which does work and fires off all the way up until the aforementioned send_to_server. So what is going on here?
require 'cool.io'
require 'http/parser'
require 'uri'
class Hash
def downcase_key
keys.each do |k|
store(k.downcase, Array === (v = delete(k)) ? v.map(&:downcase_key) : v)
end
self
end
end
module ShadyProxy
extend self
module ClientParserCallbacks
extend self
def on_message_complete(conn)
lambda do
puts "on_message_complete"
PluginHooks.before_request_to_server(conn)
end
end
def on_headers_complete(conn)
lambda do |headers|
conn.headers = headers
end
end
def on_body(conn)
lambda do |chunk|
conn.body << chunk
end
end
end
module PluginHooks
extend self
def before_request_to_server(conn)
# modify request here
conn.parser.headers.delete "Proxy-Connection"
conn.parser.headers.downcase_key
send_to_server(conn)
end
def send_to_server(conn)
parser = conn.parser
uri = URI::parse(parser.request_url)
l = Coolio::Loop.default
puts uri.scheme + "://" + uri.host
c = ShadyHttpClient.connect(uri.scheme + "://" + uri.host,uri.port).attach(l)
c.connection_reference = conn
c.request(parser.http_method,uri.request_uri)
l.run
end
def before_reply_to_client(conn)
end
end
class ShadyHttpClient < Coolio::HttpClient
def connection_reference=(conn)
puts "haz conneciton ref"
#connection_reference = conn
end
def connection_reference
#connection_reference
end
def on_connect
super
#never gets here
#headers = nil
#body = ''
#buffer = ''
end
def on_connect_failed
super
# never gets here either
end
def on_response_header(header)
#headers = header
end
def on_body_data(data)
puts "on data?"
#body << data
STDOUT.write data
end
def on_request_complete
puts "Headers"
puts #headers
puts "Body"
puts #body
end
def on_error(reason)
STDERR.puts "Error: #{reason}"
end
end
class ShadyProxyConnection < Cool.io::TCPSocket
attr_accessor :headers, :body, :buffer, :parser
def on_connect
#headers = nil
#body = ''
#buffer = ''
#parser = Http::Parser.new
#parser.on_message_complete = ClientParserCallbacks.on_message_complete(self)
#parser.on_headers_complete = ClientParserCallbacks.on_headers_complete(self)
#parser.on_body = ClientParserCallbacks.on_body(self)
end
def on_close
puts "huh?"
end
def on_read(data)
#buffer << data
#parser << data
end
end
module Server
def run(opts)
begin
# Start our server to handle connections (will raise things on errors)
l = Coolio::Loop.new
#socket = Cool.io::TCPServer.new(opts[:host],opts[:port], ShadyProxy::ShadyProxyConnection)
#socket.attach(l)
l.run
# Handle every request in another thread
loop do
Thread.new s = #socket.accept
end
# CTRL-C
rescue Interrupt
puts 'Got Interrupt..'
# Ensure that we release the socket on errors
ensure
if #socket
#socket.close
puts 'Socked closed..'
end
puts 'Quitting.'
end
end
module_function :run
end
end
ShadyProxy::Server.run(:host => '0.0.0.0',:port => 1234)

Undefined local variable error with simple program that adds items to array

class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
#name = name,
#book_id = book_id
end
end
class BookCollection
def intialize
#book_names = []
end
def add_to_books(book_name)
book_name.push(book_names)
end
end
book1 = Books.new("catch22", "12345")
book_collection1 = BookCollection.new
book_collection1.add_to_books(book1.name)
puts book_collection1
end
That is my code and the error I'm getting is "undefined local variable or method `book_names'". I tried adding " attr_accessor :book_names" and when I do that the printed output doesn't make sense.
There are a few mistakes in your code:
line 4 should not end with a comma.
initialize in class BookCollection is misspelled, resulting in #book_names not being initialized. #book_names therefore equals nil when you attempt to add an element to it with push. nil does not have a method push; hence the exception, and the message printed with the exception.
book_name.push(book_names) should be #book_name.push(book_name). (#book_name must be an instance_variable, as opposed to a local variable, to be visible outside a method, within the class definition.
puts book_collection1 prints the class instance; you want to print #book_names.
Here I've fixed your code. I've used << instead of push. Either is OK, but the former seems to be favored my most.
class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
puts "name = #{name}, book_id = #{book_id}"
#name = name
#book_id = book_id
end
end
class BookCollection
attr :book_names
def initialize
#book_names = []
end
def add_to_books(book_name)
#book_names << book_name
end
end
book_collection1 = BookCollection.new
book1 = Books.new("Catch22", "12345")
book2 = Books.new("Hawaii", "67890")
book_collection1.add_to_books(book1.name)
book_collection1.add_to_books(book2.name)
book_collection1.book_names # => ["Catch22", "Hawaii"]
Probably just a typo at
book_name.push(book_names)
Should have been
book_names.push(book_name)
With attr_accessor :book_names

Rails Activerecord observer not working

In rails 3.1 app,
I am trying to create an observable activerecord observer, but seems it doesn't work.
Even creating only activerecord observer without being observable, the after_create
event is not called, the string "in after create event" is never printed out.
The caller is a rake task.
Here's the code sample to make it clear.
class PostTemp < ActiveRecord::Base
end
class PostTempObserver < ActiveRecord::Observer
include Observable
attr_reader :new_data
def initialize
#new_data = 0
end
def after_create(record)
#binding.pry
puts "in after create event"
#new_data = 1
notify_observers(#new_data)
#new_data = 0
end
def reset_new_data
#new_data = 0
end
end
class Notifier
attr_reader :total_new_data
def initialize(model_observer)
model_observer.add_observer(self)
#total_new_data = 0
end
def update(new_data_flag)
#total_new_data = #total_new_data + new_data_flag
end
def perform
if #total_new_data > 0
#send notification
puts "notify to bar app..."
#total_new_data = 0
else
puts "no new data"
end
end
end
#in config/application.rb
config.active_record.observers = :post_temp_observer
in task1.rake
namespace :foo do
desc "crawl message and notify if there's new data"
task :get_message => :environment do
post_temp_observer = PostTempObserver.instance
notifier = Notifier.new(post_temp_observer)
#..
#do some crawling
#..
notifier.perform
end
end
I figured it out myself.
I need to add super and changed in PostTempObserver
class PostTempObserver < ActiveRecord::Observer
include Observable
attr_reader :new_data
def initialize
super
#new_data = 0
end
def after_create(post_temp)
#new_data = 1
changed
notify_observers(#new_data)
#new_data = 0
end
private
def reset_new_data
#new_data = 0
end
end

How do I using instance variables from within a lambda/Proc defined in a class variable?

I wrote the following code:
class Actions
def initialize
#people = []
#commands = {
"ADD" => ->(name){#people << name },
"REMOVE" => ->(n=0){ puts "Goodbye" },
"OTHER" => ->(n=0){puts "Do Nothing" }
}
end
def run_command(cmd,*param)
#commands[cmd].call param if #commands.key?(cmd)
end
def people
#people
end
end
act = Actions.new
act.run_command('ADD','joe')
act.run_command('ADD','jack')
puts act.people
This works, however, when the #commands hash is a class variable, the code inside the hash doesn't know the #people array.
How can I make the #commands hash be a class variable and still be able to access the specific object instance variables?
You could use instance_exec to supply the appropriate context for the lambdas when you call them, look for the comments to see the changes:
class Actions
# Move the lambdas to a class variable, a COMMANDS constant
# would work just as well and might be more appropriate.
##commands = {
"ADD" => ->(name) { #people << name },
"REMOVE" => ->(n = 0) { puts "Goodbye" },
"OTHER" => ->(n = 0) { puts "Do Nothing" }
}
def initialize
#people = [ ]
end
def run_command(cmd, *param)
# Use instance_exec and blockify the lambdas with '&'
# to call them in the context of 'self'. Change the
# ##commands to COMMANDS if you prefer to use a constant
# for this stuff.
instance_exec(param, &##commands[cmd]) if ##commands.key?(cmd)
end
def people
#people
end
end
EDIT Following #VictorMoroz's and #mu's recommendations:
class Actions
def initialize
#people = []
end
def cmd_add(name)
#people << name
end
def cmd_remove
puts "Goodbye"
end
def cmd_other
puts "Do Nothing"
end
def people
p #people
end
def run_command(cmd, *param)
cmd = 'cmd_' + cmd.to_s.downcase
send(cmd, *param) if respond_to?(cmd)
end
end
act = Actions.new
act.run_command('add', 'joe')
act.run_command(:ADD, 'jill')
act.run_command('ADD', 'jack')
act.run_command('people') # does nothing
act.people
Or
class Actions
ALLOWED_METHODS = %w( add remove other )
def initialize
#people = []
end
def add(name)
#people << name
end
def remove
puts "Goodbye"
end
def other
puts "Do Nothing"
end
def people
p #people
end
def run_command(cmd, *param)
cmd = cmd.to_s.downcase
send(cmd, *param) if ALLOWED_METHODS.include?(cmd)
end
end
act = Actions.new
act.run_command('add', 'joe')
act.run_command(:add, 'jill')
act.run_command('add', 'jack')
act.run_command('people') # does nothing
act.people

Resources