I am new to ruby and would love some help please :)
I fixed the error in my ruby code, but I'm confused as to WHY the fix works.
I get the following error private method `select' called for nil:NilClass (NoMethodError) when I type in the following line:
scanned = #keywords.select { |key| key.match(word) }
, which is located within the following method:
def find(word)
return {} unless #entries.any?
scanned = #keywords.select { |key| key.match(word) }
found ={}
if scanned.any?
scanned.each do |x| found.merge!( { x => #entries[x] } )
end
else
{}
end
found
end
,yet this error goes away if I replace the erroneous line above with the following:
scanned = #entries.keys.sort.select { |key| key.match(word) }
I define #keywords as #keywords = #entries.keys.sort in the class structure below... Why can't I use '.select' on #keywords yet I can use it on #keywords' contents?
class Dictionary
def initialize
#entries = {}
end
def entries
#entries
end
def add(entry)
#entries = entry.is_a?(Hash) ? #entries.merge!(entry) : #entries.merge!( {entry => nil} )
end
def keywords
#keywords = #entries.keys.sort
end
def include?(keyword)
#entries.keys.include?(keyword)
end
def find(word)
return {} unless #entries.any?
scanned = #keywords.select { |key| key.match(word) }
found ={}
if scanned.any?
scanned.each do |x| found.merge!( { x => #entries[x] } )
end
else
{}
end
found
end
def printable
printable = #entries.sort.map do |key, value| %Q{[#{key}] "#{value}"}
end
printable.join("\n")
end
end
You are never calling the method keywords, and thus, you are never executing the code that populates #keywords with data. It remains nil.
#keywords will remain nil until you call the method keywords, which will initialize #keywords to be the sorted entry keys.
It looks like you're calling find on a dictionary object without first calling keywords. This would result in a nil value for #keywords.
Related
Unfortunately, I get the following error. I can't quite understand why it doesn't work?
:14:in `convert': undefined method `factors' for 30:Fixnum (NoMethodError)
from question_stack.rb:18:in `<main>'
I try to create the following class:
# Class Wordgame
class Wordgame
WORDGAME_MAP = {
'3' => 'baa',
'5' => 'bar',
'7' => 'bla'
}.freeze
def self.factors
(1..self).select { |n| (self % n).zero? }
end
def self.convert(number)
number.factors.map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
end
Wordgame.convert(30)
What am I doing wrong? Where is my mental error?
self refers to the class itself in a class method or to the current object in an instance method. In your case it refers to WordGame, the object's class.
If you really want it to refer to 30 into the factors method you have to define it as an instance method, because called on an object (30), not a class (Integer), opening the Integer class
class Integer
def factors
(1..self).select { |n| (self % n).zero? }
end
end
I think you know the alternative:
def self.factors(x)
(1..x).select { |n| (self % n).zero? }
end
def self.convert(number)
factors(number).map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
Or better, with OOP.
class WordGame
def initialize(n)
#n = n
end
def convert
factors.map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
private
def factors
(1..#n).select { |m| (#n % m).zero? }
end
end
Wordgame.new(30).convert
Is that ever possible to initialize an Object instance, perform an operation on it and return this instance without creating a temporary variable?
For example:
def create_custom_object
Object.new do |instance|
instance.define_singleton_method(:foo) { 'foo' }
end
end
# returns an instance, but defines nothing :(
def create_custom_object
Object.new do
self.define_singleton_method(:foo) { 'foo' }
end
end
# same thing
rather than:
def create_custom_object
object = Object.new
object.define_singleton_method(:foo) { 'foo' }
object
end
You could use tap:
Yields self to the block, and then returns self.
Example:
def create_custom_object
Object.new.tap { |o| o.define_singleton_method(:foo) { 'foo' } }
end
object = create_custom_object
#=> #<Object:0x007f9beb857b48>
object.foo
#=> "foo"
If the argument "entry" to the method add is a hash I need to add it to the :entries hash. If "entry" is a string "entry" needs to be set as a key in the hash and its value to nil. I have a solution below but is there a cleaner way to do this?
class Test
attr_accessor :entries
def initialize
#entries = {}
end
def add(entry)
if entry.is_a?(Hash)
entry.each do |word, definition|
#entries[word] = definition
end
else
#entries[entry] = nil
end
end
end
#test = Test.new
#test.add("the")
#{"the" => nil}
#test.add("the" => "one")
#{"the"=>"one"}
I refactored the code:
class Test
attr_accessor :entries
def initialize
#entries = {}
end
def add(entry)
entry.is_a?(Hash) ? #entries.merge!(entry) : #entries[entry] = nil
end
end
I want to create a special settings class Settings. The class should be able to handle cases when a user types something like Settings.new.method_1.method_2.method_3 and it's translated to something like:
result = nil
if ConfigurationSettings['method_1'].present?
result = ConfigurationSettings['method_1']
if result['method_2'].present?
result = result['method_2']
...
end
return result
Of course, I'll make it more flexible later so it can have more than 2/3 "methods".
I guess this is the issue you are facing:
class Settings
def abc
puts "abc"
end
def xyz
puts "xyz"
end
end
s = Settings.new
s.abc
#abc
# => nil
s.xyz
#xyz
# => nil
s.abc.xyz
#abc
#NoMethodError: undefined method `xyz' for nil:NilClass
The issue here is s.abc is returning nil and xyz is called over nil. What you are trying to achieve is called Method Chaining. Now, xyz needs an Settings object. Simplest thing to do here is:
class Settings2
def abc
puts "abc"
self
end
def xyz
puts "xyz"
self
end
end
s2 = Settings2.new
s2.abc.xyz
#abc
#xyz
method_missing is available for your use and can be used to help you solve this problem. Coupling this with method chaining and you're good to go. For example:
class Settings
def method_missing(meth)
puts "Missing #{meth}"
self
end
def test
puts "Test"
self
end
end
a = Settings.new
a.test
a.test.b
a.b.test
The trouble with the other answers is all the methods return "self" so if you want to access a nested value...
final_value = Settings.new.method_1.method_2.method_3
You're just going to get the whole settings hash instead.
Try this instead...
class Settings
class SubSettings
def initialize(sub_setting)
#sub_setting = sub_setting
end
def method_missing(method, *arguments, &block)
if #sub_setting[method].is_a?(Hash)
SubSettings.new #sub_setting[method]
else
#sub_setting[method]
end
end
def answer
#sub_setting
end
end
def initialize
#settings = ConfigurationSettings
end
def method_missing(method, *arguments, &block)
SubSettings.new #settings[method]
end
end
ConfigurationSettings = {level1a: {level2a: {level3a: "hello", level3b: "goodbye"}, level2b: {level3b: "howdy"}}}
result = Settings.new.level1a.level2a.level3b
p result
=> "goodbye"
What this does is take the initial method and takes the associated sub-hash of the ConfigurationSettings hash and stored it into a new object of class SubSettings. It applies the next method and if the result is another sub-hash it iterates to create another SubSettings, etc. It only returns the actual result when it no longer sees hashes.
I'm working on a class that reads some sensor information and returns it as a hash. I would like to use the hash keys as accessors, but I'm not having much luck getting it work. Here are the relevant parts of my code so far:
I've tried it both with method_missing and by using the :define_method method.
attr_reader :sensor_hash
def method_missing(name, *args, &blk)
if args.empty? && blk.nil? && #sensor_hash.has_key?(name.to_s)
#sensor_hash[name.to_s]
else
super
end
end
def sensor(*sensor_to_return)
sensor_output = run_command(this_method_name)
sensor_output = sensor_output.split("\n")
sensor_output.map! { |line| line.downcase! }
unless sensor_to_return.empty?
sensor_to_return = sensor_to_return.to_s.downcase
sensor_output = sensor_output.grep(/^#{sensor_to_return}\s/)
end
#sensor_hash = Hash.new
sensor_output.each { |stat| #sensor_hash[stat.split(/\s+\|\s?/)[0].gsub(' ','_').to_sym] = stat.split(/\s?\|\s?/)[1..-1].each { |v| v.strip! } }
#sensor_hash.each do |k,v|
puts v.join("\t")
self.class.send :define_method, k { v.join("\t") }
end
return #sensor_hash
The data returned is a hash with the sensor name as the key and and the value is an array of everything else returned. My goal is to be able to call Class.sensor.sensor_name and get the output of Class.sensor[:sensor_name]. Currently, all I'm able to get is an undefined method error. Anybody have any idea what I'm doing wrong here?
Maybe OpenStruct does what you want. From the doc :"It is like a hash with a different way to access the data. In fact, it is implemented with a hash, and you can initialize it with one."
require 'ostruct'
s=OpenStruct.new({:sensor_name=>'sensor1',:data=>['something',1,[1,2,3]]})
p s.sensor_name
#=> "sensor1"
Just a quick example. Do you have any reasons to not monkey-patch your Hash?
irb(main):001:0> class Hash
irb(main):002:1> def method_missing(name, *args, &blk)
irb(main):003:2> if self.keys.map(&:to_sym).include? name.to_sym
irb(main):004:3> return self[name.to_sym]
irb(main):005:3> else
irb(main):006:3* super
irb(main):007:3> end
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):012:0> h = {:hello => 'world'}
=> {:hello=>"world"}
irb(main):013:0> h.hello
=> "world"
You could use a wrapper class with method missing so you don't have to monkey patch Hash.
class AccessibleHash
def initialize(hash)
#hash = hash
end
def method_missing(name, *args, &block)
sname = name.to_sym
if #hash.keys.include? sname
return #hash[sname]
else
super
end
end
end
Or, if you are working with Rails it has some nice built in object delegation with SimpleDelegator. That would allow you to define accessors on your hash as well as any nested hashes within it.
class AccessibleHash < SimpleDelegator
def initialize
define_accessors(self.keys)
end
def define_accessors(keys)
keys.each do |key|
defind_accessors(body[key].keys)
self.define_singleton_method(key) { self[key] }
end
end
end
ah = AccessibleHash.new({ some: 'hash', with: { recursive: 'accessors' })
ah.with.recursive == 'accessors'
=> true
This would be less performant at instantiation than method_missing, because it has to run recursively over your delegatee object as soon as it's created. However, it's definitely safer than method_missing, and certainly way safer than monkey patching your Hash class. Of course, safety is relative to your goals, If it's all your application does then monkey patch away.
And if you want the recursive, nested accessors without rails you could do something like this with a combination of the above...
class AccessibleHash
def initialize(hash)
#hash = hash
define_accessors(#hash.keys)
end
def define_accessors(keys)
keys.each do |key|
#hash[key] = self.class.new(#hash[key]) if #hash.keys.present?
self.define_singleton_method(key) { self[key] }
end
end
end
But at that point you're getting pretty crazy and it's probably worth reevaluating your solution in favor of something more Object Oriented. If I saw any of these in a code review it would definitely throw up a red flag. ;)