Having trouble with send and define_method - ruby

I'm trying to create a custom attr_accessor, but can't seem to get it to work. Instead of returning the value assigned to the writer, it returns the instance variable. Any ideas?
class Object
def custom_attr_accessor(klass, attribute)
ivar = "##{attribute}".to_sym
writer_body = lambda { |arg| instance_variable_set(ivar, arg) }
reader_body = lambda { ivar }
klass.send(:define_method, "#{attribute}=".to_sym, &writer_body)
klass.send(:define_method, "#{attribute}".to_sym, &reader_body)
end
end
class Person
end
custom_attr_accessor(Person, :age)
me = Person.new
me.age = 100
puts me.age
=> #age

Just like you did a instance_variable_set, you need instance_variable_get:
reader_body = lambda { instance_variable_get(ivar) }
BTW, extending Object and passing a class is not very pretty. Try to make it Persion. custom_attr_accessor(:age), that would be much more OOP.

Related

Unit testing a method in which I am doing a dependency injection

I have a terminal app with two classes: Todo and TodoApp. The method below lives in TodoApp, I would like to unit test this method and keep it isolated in my test. Since I am doing a dependency injection within the method, how could I mock that? (#todos is an empty array in TodoApp initialize)
def add(task, day, todo = Todo)
#todos.push(todo.new(task, day))
return "#{task} was added to your todos"
end
Thanks in advance for your help.
I imagine your code looks like this.
class TodoApp
def initialize
#todos = []
end
def add(task, day, todo = Todo)
#todos.push(todo.new(task, day))
return "#{task} was added to your todos"
end
end
This is NOT injecting the empty array into the TodoApp. And, therefore you are going to have difficulty accessing it from the tests.
But, if your TodoApp object looked like this:
class TodoApp
def initialize(todos = [])
#todos = todos
end
def add(task, day, todo = Todo)
#todos.push(todo.new(task, day))
return "#{task} was added to your todos"
end
end
Now you are injecting something into TodoApp that can be mocked, or even just evaluated:
describe TodoApp do
subject(:app) { described_class.new(todos) }
let(:todos) { [] }
describe '#add' do
subject(:add) { app.add(task, day) }
let(:task) { 'task' }
let(:day) { 'day' }
it 'pushes the item on the list of todos' do
expect { add }.to change { todos.length }.by(1)
end
end
end

Is there a ruby equivalent to the php __invoke magic method?

Is there an equivalent to the php __invoke method in ruby?
e.g
class Test {
public function __invoke() {
echo "invoked";
}
}
$test = new Test();
$test(); // prints invoked
Not the same exact thing but it should do the job
class Test
def self.call
puts "invoked self.call"
return new
end
def call
puts "invoked call"
end
end
t = Test.()
t.()
You can use the .() syntax on both classes and objects, since classes are objects. .() is just a shorthand for .call

Ruby send a method call to a block

I was wondering if you could yield a method call in Ruby. What I would like to do is the following:
do_in_thread { send_http_request(result) }
def do_in_thread
Thread.new { yield }
end
I basically want that method to be called in a Thread if the method is inside the block of the method do_in_thread().
Isn't it already doing so?
def do_in_thread
Thread.new { yield }
end
puts Thread.current.id
# 70059539355960
do_in_thread { puts Thread.current.id }
# 70059432631980

Ruby Quick Class Instance Scope

I have a quick problem that probably comes down to something stupid. I have a class that extends OAuth::AccessToken and uses instance variables (#) so that each time it constructs an object, those variables will be unique that instance. However, when I try to return the final object from this class, I get an error. A quick example:
require 'oauth'
class OauthFigshare < OAuth::AccessToken
def initialize (consumerkey, consumersecret, accesstoken, accesstokensecret)
#consumerkey = consumerkey
#consumersecret = consumersecret
#accesstoken = accesstoken
#accesstokensecret = accesstokensecret
#apiurl = "http://api.figshare.com"
#consumer = OAuth::Consumer.new(#consumerkey,#consumersecret,{:site=> #apiurl})
#token = { :oauth_token => #accesstoken, :oauth_token_secret => #accesstokensecret}
puts #consumer.class
puts #token
#client = OAuth::AccessToken.from_hash(#consumer, #token)
puts #client
puts #client.get('/v1/my_data/articles')
return #client
end
end
The problem is that when I check inside the class to see if the token is working, it does. However, when I check against the constructed object outside the class, it doesn't work.
#client.get(url) returns Net::HTTPOk calling from in the class
auth = OauthFigshare.new(inputs)
auth.get(url)
This returns Net::HTTPUnauthorized
What am I not getting about scope here?
Edit to include actual class
The return value of the initialize method is not used. It seems like you actually want to override self.new instead.

How can I assert on initialize behaviour with RSpec?

I have a message class, which can be initialized by passing arguments into the constructor, or by passing no arguments and then setting the attributes later with accessors. There is some pre-processing going on in the setter methods of the attributes.
I've got tests which ensure the setter methods do what they're supposed to, but I can't seem to figure out a good way of testing that the initialize method actually calls the setters.
class Message
attr_accessor :body
attr_accessor :recipients
attr_accessor :options
def initialize(message=nil, recipients=nil, options=nil)
self.body = message if message
self.recipients = recipients if recipients
self.options = options if options
end
def body=(body)
#body = body.strip_html
end
def recipients=(recipients)
#recipients = []
[*recipients].each do |recipient|
self.add_recipient(recipient)
end
end
end
I would tend to test the behaviour of the initializer,
i.e. that its setup the variables how you would expect.
Not getting caught up in the actuality of how you do it, assume that the underlying accessors work, or alternatively you could set the instance variables if you wanted. Its almost a good old fashioned unit test.
e.g.
describe "initialize" do
let(:body) { "some text" }
let(:people) { ["Mr Bob","Mr Man"] }
let(:my_options) { { :opts => "are here" } }
subject { Message.new body, people, my_options }
its(:message) { should == body }
its(:recipients) { should == people }
its(:options) { should == my_options }
end

Resources