Ruby // Nokogiri trying to store selectors for various objects in a hash
Solution 1:
css('div.class_1').css('div.class_2').text
is self.css('div.class_1').css('div.class_2').text
and self
is your Database
class. It doesn't have a css method. You need to call the method on something which has a css method like a Nokogiri node.
Callbacks
If you want to store a set of methods to call on some Nokogiri node you'll get later, you make a callback using a little anonymous function called a lambda.
@letters = {
"a" => {
uppercase: "A",
history: ->(node) { node.css('div.class_1').css('div.class_2').text },
url: "www.alphabet.com"
}
}
That takes a node as an argument and calls the methods on the node.
Then later when you have a node you can call this function.
@letters_hash[letter][:history].call(node)
Objects
At this point it's getting compliated and should be encapsulated in an object.
class LetterTopic
def initialize(letter)
@letter = letter
end
def node_history(node)
node.css('div.class_1').css('div.class_2').text
end
def uppercase
@letter.upcase
end
def url
"www.alphabet.com"
end
end
letters = {
"a" => LetterTopic.new("a")
}
node = ...get a Nokogiri node...
letters[letter].node_history(node)
A Note About Class Variables
@@letters_hash
does not do what you think. Class variables in Ruby are shared by subclasses. If you subclass Database they will all share a single @@letters_hash
variable.
class Database
@@letters = {}
def self.letters
@@letters
end
end
class Databasement < Database
end
Database.letters[:a] = 'database'
Databasement.letters[:a] = 'databasement'
p Database.letters # {:a=>"databasement"}
p Databasement.letters # {:a=>"databasement"}
Instead, use Class Instance Variables. Like everything else in Ruby, the Database class is an object and can have its own instance variables.
class Database
# Everything inside `class << self` works on the class object.
class << self
def letters
@letters ||= {}
end
end
end
class Databasement < Database
end
Database.letters[:a] = 'database'
Databasement.letters[:a] = 'databasement'
p Database.letters # {:a=>"database"}
p Databasement.letters # {:a=>"databasement"}