Rdoc: How to document delegation? - ruby

My class delegates a method to another object (which I'll call the helper object). I want to include documentation of that method in the delegating class, not just the delegated class. The programmer should not use the helper object, only the main object, so documenting in the helper object isn't very useful.
Consider this example. Rdoc outputs documentation about Node#initialize and Helper#hello. I want to have documentation about Node#hello as if it were just another method. Is there a way to do that?
require 'forwardable'
class Node
extend Forwardable
delegate %w(hello) => :#helper
# Takes no params.
def initialize
#helper = Helper.new()
end
end
class Helper
# Outputs 'hello world'
def hello
puts 'hello world'
end
end
-- update --
I tried yard. The yard command looks like this:
yardoc --no-private --protected app/**/*.rb -
I edited the ruby code to try to add documentation for Node#hello:
class Node
extend Forwardable
delegate %w(hello) => :#helper
# Takes no params.
def initialize
#helper = Helper.new()
end
# #!method hello
# Documentation for Node#hello
end
When I run yardoc, it seems to say that it processes three methods:
Files: 1
Modules: 0 ( 0 undocumented)
Classes: 2 ( 2 undocumented)
Constants: 0 ( 0 undocumented)
Attributes: 0 ( 0 undocumented)
Methods: 3 ( 0 undocumented)
60.00% documented
If I don't have the # #!method directive then it says it only processes two methods:
Files: 1
Modules: 0 ( 0 undocumented)
Classes: 2 ( 2 undocumented)
Constants: 0 ( 0 undocumented)
Attributes: 0 ( 0 undocumented)
Methods: 2 ( 0 undocumented)
50.00% documented
So it seems like it seems that documentation for Node#hello, but it doesn't appear in the actual documentation:
So I'm not sure where to go from there.

On advice from #max, I switched to yard. Using yard, I can create method documentation without any actual method like this:
# #!method hello()
# Outputs "hello world"
delegate %w(hello) => :#helper
It's important to note that the documentation has to be above actual code. It won't work if it's by itself.

Related

Uniqueness of symbols in object methods

Given that symbols are unique, how is it possible that the two instances of:test below, which refer to different methods (in different classes), have the same object ID?
class Dope
def test
end
end
class Green
def test
end
end
green = Green.new
dope = Dope.new
green.methods.include?(:test) # => true
dope.methods.include?(:test) # => true
green.test.object_id # => 8
dope.test.object_id # => 8
How can you have two symbols with the same name/object ID that refer to different methods?
Both of your test methods return nil. So the object id that you get is the object id of nil:
nil.object_id
# => 8
A symbol is stored differently than a string. Any existence of :test is always going to equal :test because symbols are immutable, unlike strings. There are more descriptions out there here or here.
The :test used to determine the presence of that method will be the same symbol :test that you store in a different class for params[:test] = "something.
Another thing to note that when you call a method on a ruby object, you are technically sending a message to that class. So you're example shows that they both respond to the message :test. Which will call the method on the class. This is accurate.

why is before :save callback hook not getting called from FactoryGirl.create()?

This simple example uses DataMapper's before :save callback (aka hook) to increment callback_count. callback_count is initialized to 0 and should be set to 1 by the callback.
This callback is invoked when the TestObject is created via:
TestObject.create()
but the callback is skipped when created by FactoryGirl via:
FactoryGirl.create(:test_object)
Any idea why? [Note: I'm running ruby 1.9.3, factory_girl 4.2.0, data_mapper 1.2.0]
Full details follow...
The DataMapper model
# file: models/test_model.rb
class TestModel
include DataMapper::Resource
property :id, Serial
property :callback_count, Integer, :default => 0
before :save do
self.callback_count += 1
end
end
The FactoryGirl declaration
# file: spec/factories.rb
FactoryGirl.define do
factory :test_model do
end
end
The RSpec tests
# file: spec/models/test_model_spec.rb
require 'spec_helper'
describe "TestModel Model" do
it 'calls before :save using TestModel.create' do
test_model = TestModel.create
test_model.callback_count.should == 1
end
it 'fails to call before :save using FactoryGirl.create' do
test_model = FactoryGirl.create(:test_model)
test_model.callback_count.should == 1
end
end
The test results
Failures:
1) TestModel Model fails to call before :save using FactoryGirl.create
Failure/Error: test_model.callback_count.should == 1
expected: 1
got: 0 (using ==)
# ./spec/models/test_model_spec.rb:10:in `block (2 levels) in <top (required)>'
Finished in 0.00534 seconds
2 examples, 1 failure
At least for factory_girl 4.2 (don't know since which version it is supported), there is another workwaround through the use of custom methods to persist objects. As it is stated in a response to an issue about it in Github, it is just a matter of calling save instead of save!.
FactoryGirl.define do
to_create do |instance|
if !instance.save
raise "Save failed for #{instance.class}"
end
end
end
Of course it is not ideal because it should be functional in FactoryGirl core, but I think right now it is the best solution and, at the moment, I'm not having conflicts with other tests...
The caveat is that you have to define it in each factory (but for me it wasn't an inconvenient)
Solved.
#Jim Stewart pointed me to this FactoryGirl issue where it says "FactoryGirl calls save! on the instance [that it creates]". In the world of DataMapper, save! expressly does not run the callbacks -- this explains the behavior that I'm seeing. (But it doesn't explain why it works for #enthrops!)
That same link offers some workarounds specifically for DataMapper and I'll probably go with one of them. Still, it would be nice if an un-modified FactoryGirl played nice with DataMapper.
update
Here's the code suggested by Joshua Clayton of thoughtbot. I added it to my spec/factories.rb file and test_model_spec.rb now passes without error. Cool beans.
# file: factories.rb
class CreateForDataMapper
def initialize
#default_strategy = FactoryGirl::Strategy::Create.new
end
delegate :association, to: :#default_strategy
def result(evaluation)
evaluation.singleton_class.send :define_method, :create do |instance|
instance.save ||
raise(instance.errors.send(:errors).map{|attr,errors| "- #{attr}: #{errors}" }.join("\n"))
end
#default_strategy.result(evaluation)
end
end
FactoryGirl.register_strategy(:create, CreateForDataMapper)
update 2
Well. perhaps I spoke too soon. Adding the CreateForDataMapper fixes that one specific test, but appears to break others. So I'm un-answering my question for now. Someone else have a good solution?
Use build to build your object, then call save manually...
t = build(:test_model)
t.save

Redefine a method for unit tests

I'm not so string in ruby syntax, but if I understand something when learning it, you can do everything, no restrictions, lets say I got a class method, and I want to redefine it in a unit test to isolate the stuff I'm testing, how do i do that? lets say class name is foo and instance method is bar.
Test::Redef is a gem designed to do exactly this.
test "some method on Foo should be called because under normal circumstances it something useful like send network traffic" do
Test::Redef.rd(
'Foo#bar' => proc {|args| puts 'new behavior goes here!'},
'Foo#baz' => :empty, # same as => proc { }
'Foo#quux' => :wiretap, # no behavior change but you get invocation tracking
'Foo.blat' => :empty, # class method
) do |rd|
# code under test
assert_equal [['invocation 1 arg 1', 'invocation 1 arg 2'],
['invocation 2 arg 1'],
], rd[:bar].args, 'Foo#bar was called with the right arguments'
end
# now all methods are back to normal
end

RDoc not generating source files

I'm using Rdoc 2.58 with Ruby 1.9.2p138. When I generate my rdoc files using the Darkfish format (which is the only option), I'm not getting the source code previews with it. There are no error messages, and the HTML files are being generated for all the classes.
I've tried updating the gem and reading the help files, but I'm not seeing any way to fix this problem.
Is this a known issue? What else can I try?
UPDATE:
example code:
class Iamaclass
def initialize
#name = "superclass"
end
def get_name
#name
end
def proud?
true
end
end
Rdoc 2.5.8 outputs
Parsing sources...
100% [ 1/ 1] someruby.rb
Generating Darkfish...
Files: 1
Classes: 1 ( 1 undocumented)
Constants: 0 ( 0 undocumented)
Modules: 0 ( 0 undocumented)
Methods: 3 ( 3 undocumented)
0.00% documented
Elapsed: 0.0s
source code does not toggle in generated rdoc.
Rdoc 3.5.3 outputs
100% [ 1/ 1] someruby.rb
Generating Darkfish format into /Users/paul/dev/alesrelated/someruby/doc...
Files: 1
Classes: 1 (1 undocumented)
Modules: 0 (0 undocumented)
Constants: 0 (0 undocumented)
Attributes: 0 (0 undocumented)
Methods: 3 (3 undocumented)
Total: 4 (4 undocumented)
0.00% documented
Elapsed: 0.1s
source code does toggle in html output.
You need to add comments to your code, you can try using this for example:
# A description about this class
class Iamaclass
# A description about this method
def initialize
#name = "superclass"
end
# Another description about some other method
def get_name
#name
end
def proud?
true
end
end
After this you can try running the rdoc command again.

Mock system call in ruby

Know of a way to mock %[]? I'm writing tests for code that makes a few system calls, for example:
def log(file)
%x[git log #{file}]
end
and would like to avoid actually executing system calls while testing this method. Ideally I'd like to mock %x[..] and assert that the correct shell command is passed to it.
%x{…} is Ruby built-in syntax that will actually call Kernel method Backtick (`). So you can redefine that method. As backtick method returns the standard output of running cmd in a subshell, your redefined method should return something similar to that ,for example, a string.
module Kernel
def `(cmd)
"call #{cmd}"
end
end
puts %x(ls)
puts `ls`
# output
# call ls
# call ls
Using Mocha, if you want to mock to following class:
class Test
def method_under_test
system "echo 'Hello World!"
`ls -l`
end
end
your test would look something like:
def test_method_under_test
Test.any_instance.expects(:system).with("echo 'Hello World!'").returns('Hello World!').once
Test.any_instance.expects(:`).with("ls -l").once
end
This works because every object inherits methods like system and ` from the Kernel object.
I don't know of a way to mock a module, I'm afraid. With Mocha at least, Kernel.expects doesn't help. You could always wrap the calling in a class and mock that, something like this:
require 'test/unit'
require 'mocha'
class SystemCaller
def self.call(cmd)
system cmd
end
end
class TestMockingSystem < Test::Unit::TestCase
def test_mocked_out_system_call
SystemCaller.expects(:call).with('dir')
SystemCaller.call "dir"
end
end
which gives me what I'd hope for:
Started
.
Finished in 0.0 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
How about logging it to a text file, or outputting it to your console?
def log(file)
puts "git log #{file}"
end
Can't you just ovverride the function with a method that returns true when it gets the command?

Resources