Instance method not able to act on instance variable - ruby

Even though I required Nokogiri and initialized the variable, my method does not have access to the Nokogiri methods. I want to do this:
class Requester
require 'nokogiri'
def initialize(body)
#body = body
end
def destination
#body.at_css('destination')
end
end
and then I pass body, which is a Nokogiri document.
mess = Requester.new(body)
When I do this I get a "No Method Error":
mess.destination
I don't understand. I thought my class would have all the Nokogiri methods if I require it.
The full error is on at_css and looks like:
NoMethodError (undefined method `at_css' for #<Requester:0x007f685d971478>

You are confusing require and include.
require loads a file or a gem.
include includes the methods of another object.
a.rb:
module A
def hello
"hello world"
end
end
b.rb:
require 'a'
class B
include A
end
puts B.new.hello # hello world
However, you really need to rethink what you are trying to do. You can't include a class - you extend classes. And the object you are looking for is the class Nokogiri::HTML::Document.
If you are trying to build a document crawler you can use the delegator pattern:
require 'nokogiri'
class Requester < Delegator
def initialize(body)
super
#body = body
#doc = Nokogiri::HTML(body)
end
def __getobj__
#doc
end
end
Or you would create a subclass of Nokogiri::HTML::Document.
http://www.nokogiri.org/tutorials/searching_a_xml_html_document.html
http://ruby-doc.org/stdlib-2.2.1/libdoc/delegate/rdoc/Delegator.html

Related

Ruby inheritance, method not being passed to a child class

I've got a ruby exercise, and cannot quite get past one point.
When I run the tests, it throws undefined method "attribute" for CommentSerializer:Class.
Though, there is such a method defined in serializer.rb, from which it's being inherited.
Am I missing something about inheritance in ruby here?
Note: I am neither allowed to add any gems other than the two listed below, nor to modify any file other than serializer.rb.
Here are the files:
Gemfile:
gem 'rspec'
gem 'pry'
app/comment.rb:
Comment = Struct.new(:id, :body)
app/comment_serializer.rb:
require_relative "serializer"
class CommentSerializer < Serializer
attribute :id
attribute :body
end
app/serializer.rb:
class Serializer
def initialize(object)
#obj = object
end
def serialize
obj.members.inject({}) do |hash, member|
hash[member] = obj[member]
hash
end
end
def attribute(key)
end
private
def obj
#obj
end
end
spec/comment_serializer_spec.rb:
require "date"
require_relative "spec_helper"
require_relative "../app/comment"
require_relative "../app/comment_serializer"
RSpec.describe CommentSerializer do
subject { described_class.new(comment) }
let(:comment) do
Comment.new(1, "Foo bar")
end
it "serializes object" do
expect(subject.serialize).to eq({
id: 1,
body: "Foo bar",
})
end
end
If you call something like attribute in the body of the class definition then it happens in the class context at that exact moment, as in:
class Example < Serializer
# This is evaluated immediately, as in self.attribute(:a) or Example.attribute(:a)
attribute :a
end
There must be a corresponding class method to receive that call, as in:
class Serializer
def self.attribute(name)
# ...
end
end
Since you're inheriting that method it will be defined prior to calling it, but that's not the case if you have something like:
class Example
attribute :a # undefined method `attribute' for Example:Class (NoMethodError)
def self.attribute(name)
end
end
The method is defined after it's called, so you get this error. You must either reverse the order, define first, call second, or put it into a parent class.

Ruby instance_eval confusion with load`

I am trying to test a single method in ruby. It is in a separate file so basically:
a.rb:
def my_method
...
end
in my a_spec.rb
require 'minitest/autorun'
Object.instance_eval do
load("path_to/a.rb")
def hello_world
...
end
end
When I try to run my test, it says that my_method is a private method while I can actually call Object.hello_world outright. What gives?
Also, is there an easier way to test plain ruby methods(no classes or modules) with minitest?
Doing the load above doesn't add the methods of a.rb as singleton methods to Object. Rather, it adds the methods to the global namespace. (The fact that you are doing the load inside the block where self refers to the Object class is irrelevant.)
With you above code, you should be able to call *my_method* directly in your tests:
class MyTest < MiniTest::Unit::TestCase
def test_my_method
assert my_method
end
def test_hello_world
assert Object.hello_world
end
end

How can I modify this class to use the singleton pattern, like activemodel?

I have a httparty "model" which I use like so
myRest = RestModel.new
myRest.someGetResquest()
myRest.somePostRequest()
How would I go about changing it to work similarly to an activemodel, like so?
RestModel.someGetRequest()
RestModel.somePostRequest()
this blog post shows how to include the singleton module but its still accesses the instance like this: RestModel.instance.someGetRequest()
here is my code:
class Managementdb
include HTTParty
base_uri "http://localhost:7001/management/"
def initialise(authToken)
self.authToken = authToken
end
def login()
response = self.class.get("/testLogin")
if response.success?
self.authToken = response["authToken"]
else
# this just raises the net/http response that was raised
raise response.response
end
end
attr_accessor :authToken
...
end
Please tell me that I am doing it all wrong (show me the light)
You want to use extend rather than include, which will add the methods onto the class singleton, rather than making them available on instances.
class Managementdb
extend HTTParty
end
a longer example illustrating this:
module Bar
def hello
"Bar!"
end
end
module Baz
def hello
"Baz!"
end
end
class Foo
include Bar
extend Baz
end
Foo.hello # => "Baz!"
Foo.new.hello # => "Bar!"

HTTParty - JSON to strongly typed object

Is it possible to have HTTParty deserialize the results from a GET to a strongly typed ruby object? For example
class Myclass
include HTTParty
end
x = Myclass.get('http://api.stackoverflow.com/1.0/questions?tags=HTTParty')
puts x.total
puts x.questions[0].title
Right now it deserializes it into a hash
puts x["total"]
My question is actually if HTTParty supports this OTB, not by installing additional gems.
Edit:
I'm still new to Ruby, but I recall that class fields are all private so they would need to be accessed through getter/setter methods. So maybe this question isn't a valid one?
If you are just wanting method syntax, you can use an open struct.
require 'httparty'
require 'ostruct'
result = HTTParty.get 'http://api.stackoverflow.com/1.0/questions?tags=HTTParty'
object = OpenStruct.new result
object.total # => 2634237
A possible downside is that this object is totally open such that if you invoke a nonexistent method on it, it will just return nil (if you invoke a setter, it will create both the setter and getter)
It sounds like you want the return value of Myclass::get to be an instance of Myclass. If that's the case, you could cache the return value from the HTTP request and implement method_missing to return values from that hash:
class Myclass
include HTTParty
attr_accessor :retrieved_values
def method_missing(method, *args, &block)
if retrieved_values.key?(method)
retrieved_values[method]
else
super
end
end
def self.get_with_massaging(url)
new.tap do |instance|
instance.retrieved_values = get_without_massaging(url)
end
end
class << self
alias_method :get_without_massaging, :get
alias_method :get, :get_with_massaging
end
end
This isn't exactly what you asked for, because it only works one level deep — i.e., x.questions[0].title would need to be x.questions[0][:title]
x = Myclass.get('http://api.stackoverflow.com/1.0/questions?tags=HTTParty')
p x.total
p x.questions[0][:title]
Perhaps you could come up with some hybrid of this answer and Joshua Creek's to take advantage of OpenStruct.
I should also point out that all the method aliasing trickery isn't necessary if your method doesn't have to be named get.

ruby mixins errors

I'm confused with the following piece of code.
HTTParty library has class method named def self.get(..).
I include it in the Client module and then include that Client module in my Line class and access the get method in my def self.hi() method.
But when I run, it throws out the error:
ruby geek-module.rb
geek-module.rb:12:in `hi': undefined method `get' for Line:Class (NoMethodError)
from geek-module.rb:16:in `<main>'
Why I'm not being able to access that get method of HTTParty?
Following is the code:
require 'rubygems'
require 'httparty'
module Client
include HTTParty
end
class Line
include Client
def self.hi
get("http://gogle.com")
end
end
puts Line.hi
You cannot access self.get method because you use include HTTParty, include makes methods acessible by instances of class not class himself, your hi method is class method but get method is the instance method. If you use something like:
class Line
include Client
def hi
get("http://gogle.com")
end
end
line = Line.new
line.get
I think it should work
... or just use extend Client rather than include
So, when you include HTTParty in the Client module you can access the get method through Client.get. And when you include Client in the Line class you can access the get method through Client.get too. Actually, if you want to use the get method in your Line class, you don't need to include it. So:
require 'rubygems'
require 'httparty'
module Client
include HTTParty
end
class Line
def self.hi
Client.get("http://google.com")
end
end
puts Line.hi
or if you want the get method in your Line class you can use something like that:
class Client
include HTTParty
end
class Line < Client
def self.hi
get("http://google.com")
end
end
puts Line.hi

Resources