I'm getting an 'undefined method' error on the get_nums method below, and I can't figure out why. list_hostnames returns an array, but get_nums can't do anything with it. Could someone please point me in the right direction?
class Hostname
attr_accessor :hostname, :domain_controller_ip, :username, :password, :hosts
def initialize(hostname, domain_controller_ip, ad_username, ad_password)
#domain_controller_ip = domain_controller_ip
#ad_username = ad_username
#ad_password = ad_password
#hostname = hostname
#hosts = []
def list_hostnames
a = Ldap.new(#domain_controller_ip, #ad_username, #ad_password)
hostname = #hostname + "*"
a.ldap_con.search(:base => a.treebase, :filter => a.filter('cn', hostname)) do |entry|
self.hosts.push(entry.cn[0])
end
self.hosts.each do |x|
p x
end
end
def get_nums
self.hosts.each do |x|
i = x.match(/\d+$/)
p i
end
end
end
a = Hostname.new('prod-srv-1', '192.168.1.1', 'administrator', 'password')
b = a.list_hostnames
b.get_nums
end
It seems, that you've been confused by indentation and didn't correctly close the methods by end.
I think that the following code is the correct version of the code you're trying to implement:
class Hostname
attr_accessor :hostname, :domain_controller_ip, :username, :password, :hosts
def initialize(hostname, domain_controller_ip, ad_username, ad_password)
#domain_controller_ip = domain_controller_ip
#ad_username = ad_username
#ad_password = ad_password
#hostname = hostname
#hosts = []
end
def list_hostnames
a = Ldap.new(#domain_controller_ip, #ad_username, #ad_password)
hostname = #hostname + "*"
a.ldap_con.search(:base => a.treebase, :filter => a.filter('cn', hostname)) do |entry|
self.hosts.push(entry.cn[0])
end
self.hosts.each do |x|
p x
end
self
end
def get_nums
self.hosts.each do |x|
i = x.match(/\d+$/)
p i
end
end
end
a = Hostname.new('prod-srv-1', '192.168.1.1', 'administrator', 'password')
b = a.list_hostnames
b.get_nums
Since you have an attr_accessor defined for hosts, you don't need the self.hosts. You could just do:
hosts.each do |h|
# code here
end
Related
I'm writing a class MyHashSet that imitates the Set class. The elements of the set are contained as hash items reading {element => true}. Here is how it is defined:
class MyHashSet
attr_accessor :store
def initialize
#store = {}
end
def insert(el)
#store.merge!(el => true)
end
def include?(el)
#store[el]
end
def delete(el)
#store.select! {|key,value| key != el}
end
def to_a
#store.keys
end
def self.union(set)
result=MyHashSet.new
result.store=(self.store).merge(set.store)
result
end
end
The last method union should be such that if I type the commands:
set1=MyHashSet.new
set2=MyHashSet.new
set1.insert("Mark Hamill")
set1.insert("Harrison Ford")
set1.insert("Anthony Daniels")
set2.insert("Ewan McGregor")
set2.insert("Natalie Portman")
set2.insert("Anthony Daniels")
and then try to compute
set3=set1.union(set2)
I should get a set3 variable, which is an instance of MyHashSet such that its store is:
{"Mark Hamill"=>true, "Harrison Ford"=>true, "Anthony Daniels"=>true, "Ewan McGregor"=>true, "Natalie Portman"=>true}
However, if I try to run this I get an undefined method error message:
`<main>': undefined method `union' for #<MyHashSet:0x00000000f4e3b8> (NoMethodError)
I don't understand why Ruby does not pick this method.
You must use it like instance method not class method. And you can use self.class.new insted of MyHashSet.new
like this
class MyHashSet
attr_accessor :store
def initialize
#store = {}
end
def insert(el)
#store.merge!(el => true)
end
def include?(el)
#store[el]
end
def delete(el)
#store.select! {|key,value| key != el}
end
def to_a
#store.keys
end
def union(set)
result = self.class.new
result.store = self.store.merge(set.store)
result
end
end
output
set1=MyHashSet.new
# => #<MyHashSet:0x1efea79 #store={}>
set2=MyHashSet.new
# => #<MyHashSet:0x34c75a #store={}>
set1.insert("Mark Hamill")
# => {"Mark Hamill" => true}
set1.insert("Harrison Ford")
# => {"Mark Hamill" => true, "Harrison Ford" => true}
set1.insert("Anthony Daniels")
# => {"Mark Hamill" => true, "Harrison Ford" => true, "Anthony Daniels" => true}
set2.insert("Ewan McGregor")
# => {"Ewan McGregor" => true}
set2.insert("Natalie Portman")
#=> {"Ewan McGregor" => true, "Natalie Portman" => true}
set2.insert("Anthony Daniels")
# => {"Ewan McGregor" => true, "Natalie Portman" => true, "Anthony Daniels" => true}
set3 = set1.union(set2)
# => #<MyHashSet:0x1c7cbad #store={"Mark Hamill"=>true, "Harrison Ford"=>true, "Anthony Daniels"=>true, "Ewan McGregor"=>true, "Natalie Portman"=>true}>
btw:
Maybe you can modify your initialize method for better usage like this
def initialize(store = nil)
#store = store || {}
end
After this you can call union easly
def union(set)
self.class.new(self.store.merge(set.store))
end
so your final clas will looks like this
class MyHashSet
attr_accessor :store
def initialize(store = nil)
#store = store || {}
end
def insert(el)
#store.merge!(el => true)
end
def include?(el)
#store[el]
end
def delete(el)
#store.select! { |key, value| key != el }
end
def to_a
#store.keys
end
def union(set)
self.class.new(self.store.merge(set.store))
end
end
I found that the code works if I remove the "self." in front of "union":
class MyHashSet
attr_accessor :store
def initialize
#store = {}
end
def insert(el)
#store.merge!(el => true)
end
def include?(el)
#store[el]
end
def delete(el)
#store.select! {|key,value| key != el}
end
def to_a
#store.keys
end
def union(set)
result=MyHashSet.new
result.store=(self.store).merge(set.store)
result
end
end
I suppose this is because the method is to be called on an instance of the class rather than on the class itself.
There is the following code:
class MyOpenStruct
def initialize(initial_values = {})
#values = initial_values
end
def _singleton_class
class << self
self
end
end
def method_missing(name, *args, &block)
if name[-1] == "="
base_name = name[0..-2].intern
puts "add_method_to_set"
self.class.add_method_to_set(base_name)
#values[base_name] = args[0]
else
puts "add_method_to_get"
self.class.add_method_to_get(base_name)
#values[name]
end
end
def self.add_method_to_get(name)
define_method(name) do |value|
#values[name]
end
end
def self.add_method_to_set(name)
define_method(name) do |value|
#values[name] = value
end
end
end
obj1 = MyOpenStruct.new(name: "Dave")
obj1.address = "1"
obj2 = MyOpenStruct.new(name: "Dave")
obj2.address = "2"
I want to do the following thing: when I execute some method (obj1.address) and it's missing I want to add this method to my MyOpenStruct class. But when I execute my code I get 'missing' two times instead of one. Why? I don't understand. Please explain it to me. Thanks.
#koffeinfrei identified one problem with you code, but I found a few others. Below I have what I believe to be a corrected version. I have also suggested an alternative way to structure the code. My main advice is to pull out the dynamic creation of instance methods, as that is quite generic. You might even put that in a module with other methods that you could include as needed.
Your code with repairs
class MyOpenStruct
def initialize(initial_values = {})
#values = initial_values
end
def method_missing(name, *args, &block)
puts "in mm, name = #{name}"
if name[-1] == "="
base_name = name[/\w+/]
puts "add_method_to_set: '#{name}'"
self.class.add_method_to_set(base_name)
#values[base_name.to_sym] = args[0]
else
puts "add_method_to_get: '#{name}'"
self.class.add_method_to_get(name)
#values[name.to_sym]
end
end
def self.add_method_to_get(name)
define_method(name.to_sym) do
#values[name.to_sym]
end
end
def self.add_method_to_set(name)
define_method((name+'=').to_sym) do |value|
#values[name.to_sym] = value
end
end
end
Alternative construction
def create_instance_eval(klass, method, &block)
klass.class_eval { define_method(method, &block) }
end
class MyOpenStruct
def initialize(initial_values = {})
#values = initial_values
end
def method_missing(name, *args, &block)
if name[-1] == "="
base_name = name[/\w+/]
method_name = (base_name+'=').to_sym
puts "create method '#{method_name}'"
method = create_instance_eval(self.class, method_name) do |value|
#values[base_name.to_sym] = value
end
send(method, args[0])
else
method_name = name.to_sym
puts "create method '#{method_name}'"
method = create_instance_eval(self.class, method_name) do
#values[method_name]
end
send(method)
end
end
end
Example
MyOpenStruct.instance_methods(false)
#=> [:method_missing]
obj1 = MyOpenStruct.new(name: "Dave")
#=> #<MyOpenStruct:0x00000102805b58 #values={:name=>"Dave"}>
obj1.address = "1"
# create method 'address='
#=> "1"
MyOpenStruct.instance_methods(false)
#=> [:method_missing, :address=]
obj2 = MyOpenStruct.new(name: "Mitzy")
#=> #<MyOpenStruct:0x00000101848878 #values={:name=>"Mitzy"}>
obj2.address = 2
#=> 2
obj2.address
# create method 'address'
# => 2
MyOpenStruct.instance_methods(false)
$#=> [:method_missing, :address=, :address]
obj1.instance_variable_get(:#values)
#=> {:name=>"Dave", :address=>"1"}
obj2.instance_variable_get(:#values)
#=> {:name=>"Mitzy", :address=>2}
The method name for the setter method needs to have the trailing =, so you need to define the method with the name instead of the base_name.
self.class.add_method_to_set(name)
For academic reasons, I'd like to make an instance of Ruby class act like a hash.
GOALS
Initialize MyClass instance with a hash # success
Request values from instance of myClass, like a hash # success
Then set properties as a hash # fail
Although some discussion exists, I tried what's out there (1, 2) with no success. Let me know what I'm doing wrong. Thanks!
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def set_prop(key, value)
myhash[key] = value
end
end
test = myClass.new({:a => 3}) #=> #<MyClass:0x007f96ca943898 #my_hash={:a=>3}>
test[:a] #=> 3
test[:b] = 4 #=> NameError: undefined local variable or method `myhash' for #<MyClass:0x007f96ca9d0ef0 #my_hash={:a=>3}>
You declared set_prop, but you're using []= in tests. Did you mean to get this?
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
my_hash[key]
end
def []=(key, value)
my_hash[key] = value
end
end
test = MyClass.new({:a => 3}) # success
test[:a] # success
test[:b] = 4 # success
test.my_hash # => {:a=>3, :b=>4}
module HashizeModel
def [](key)
sym_key = to_sym_key(key)
self.instance_variable_get(sym_key)
end
def []=(key, value)
sym_key = to_sym_key(key)
self.instance_variable_set(sym_key, value)
end
private
def to_sym_key(key)
if key.is_a? Symbol
return ('#'+key.to_s).to_sym
else
return ('#'+key.to_s.delete('#')).to_sym
end
end
end
You should write it as test = MyClass.new({:a => 3}) and the below code should work.
class MyClass
attr_accessor :my_hash
def initialize(hash={})
#my_hash = hash
end
def [](key)
#my_hash[key]
end
def []=(key,val)
#my_hash[key]=val
end
def set_prop(key, value)
#myhash[key] = value
end
end
test = MyClass.new({:a => 3})
test[:a]
test[:b]= 4
test.my_hash # => {:a=>3, :b=>4}
how to parse the textfile like
name id
name id
and save in an array of arrays in ruby.
So far i have:
content = []
File.open("my/file/path", "r").each_line do |line|
person << line.chop
end
It gives the output as:
"name\tID", "name2\tID" ....
Here's how I'd solve this:
class Person
attr :name, :id
def initialize(name, id)
#name, #id = name.strip, id.strip
end
end
class << Person
attr_accessor :file, :separator
def all
Array.new.tap do |persons|
File.foreach file do |line|
persons.push new *line.split(separator)
end
end
end
end
Person.file = 'my/file/path'
Person.separator = /\t/
persons = Person.all
persons.each do |person|
puts "#{person.id} => #{person.name}"
end
Use ruby's String#split
pry(main)> "foo\tbar".split("\t")
=> ["foo", "bar"]
this should work:
content = []
File.open("my/file/path", "r").each_line do |line|
person << line.chop.split("\t")
end
EDIT: to create separate arrays do this:
content = []
persons = []
ids = []
File.open("my/file/path", "r").each_line do |line|
temp = line.chop.split("\t")
persons << temp[0]
ids << temp[1]
end
(Big edit, I got part of the way there…)
I've been hacking away and I've come up with this as a way to specify things that need to be done before attributes are read:
class Class
def attr_reader(*params)
if block_given?
params.each do |sym|
define_method(sym) do
yield
self.instance_variable_get("##{sym}")
end
end
else
params.each do |sym|
attr sym
end
end
end
end
class Test
attr_reader :normal
attr_reader(:jp,:nope) { changethings if #nope.nil? }
def initialize
#normal = "Normal"
#jp = "JP"
#done = false
end
def changethings
p "doing"
#jp = "Haha!"
#nope = "poop"
end
end
j = Test.new
p j.normal
p j.jp
But changethings isn't being recognised as a method — anyone got any ideas?
You need to evaluate the block in the context of the instance. yield by default will evaluate it in its native context.
class Class
def attr_reader(*params, &blk)
if block_given?
params.each do |sym|
define_method(sym) do
self.instance_eval(&blk)
self.instance_variable_get("##{sym}")
end
end
else
params.each do |sym|
attr sym
end
end
end
end
Here's another alternative approach you can look at. It's not as elegant as what you're trying to do using define_method but it's maybe worth looking at.
Add a new method lazy_attr_reader to Class
class Class
def lazy_attr_reader(*vars)
options = vars.last.is_a?(::Hash) ? vars.pop : {}
# get the name of the method that will populate the attribute from options
# default to 'get_things'
init_method = options[:via] || 'get_things'
vars.each do |var|
class_eval("def #{var}; #{init_method} if !defined? ##{var}; ##{var}; end")
end
end
end
Then use it like this:
class Test
lazy_attr_reader :name, :via => "name_loader"
def name_loader
#name = "Bob"
end
end
In action:
irb(main):145:0> t = Test.new
=> #<Test:0x2d6291c>
irb(main):146:0> t.name
=> "Bob"
IMHO changing the context of the block is pretty counter-intuitive, from a perspective of someone who would use such attr_reader on steroids.
Perhaps you should consider plain ol' "specify method name using optional arguments" approach:
def lazy_attr_reader(*args, params)
args.each do |e|
define_method(e) do
send(params[:init]) if params[:init] && !instance_variable_get("##{e}")
instance_variable_get("##{e}")
end
end
end
class Foo
lazy_attr_reader :foo, :bar, :init => :load
def load
#foo = 'foo'
#bar = 'bar'
end
end
f = Foo.new
puts f.bar
#=> bar