Skip to content

Commit

Permalink
Adding watchr script, adding some tests, reducing the complexity of t…
Browse files Browse the repository at this point in the history
…he add method.
  • Loading branch information
nathankleyn committed Jun 5, 2013
1 parent 32c453a commit 92d3481
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 68 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
/bin
/coverage
/doc
/tmp
/.bundle
/.yardoc
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ GEM
diff-lcs (1.2.4)
ffi (1.8.1)
i18n (0.6.1)
iobuffer (1.1.2)
libusb (0.3.4)
ffi (>= 1.0)
method_source (0.8.1)
Expand All @@ -35,6 +36,8 @@ GEM
redcarpet (2.3.0)
rest-client (1.6.7)
mime-types (>= 1.16)
rev (0.3.2)
iobuffer (>= 0.1.3)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
Expand All @@ -50,6 +53,7 @@ GEM
simplecov-html (0.7.1)
slop (3.4.5)
thor (0.18.1)
watchr (0.7)
yard (0.8.6.1)

PLATFORMS
Expand All @@ -60,5 +64,7 @@ DEPENDENCIES
lego-nxt!
pry (~> 0.9.12.2)
redcarpet (~> 2.3.0)
rev (~> 0.3.2)
rspec (~> 2.13.0)
watchr (~> 0.7.0)
yard (~> 0.8.6.1)
2 changes: 2 additions & 0 deletions lego-nxt.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ Gem::Specification.new do |spec|
spec.add_dependency('serialport', '~>1.1.0')
spec.add_development_dependency('coveralls', '~>0.6.7')
spec.add_development_dependency('redcarpet', '~>2.3.0')
spec.add_development_dependency('rev', '~>0.3.2')
spec.add_development_dependency('rspec', '~>2.13.0')
spec.add_development_dependency('pry', '~>0.9.12.2')
spec.add_development_dependency('watchr', '~>0.7.0')
spec.add_development_dependency('yard', '~>0.8.6.1')
end
59 changes: 31 additions & 28 deletions lib/nxt/nxt_brick.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
# nxt.disconnect
class NXTBrick
include NXT::Exceptions
include NXT::Utils::Assertions

# An enumeration of possible ports, both input and output, that the NXT brick
# can have connectors attached to.
Expand Down Expand Up @@ -90,45 +91,28 @@ def disconnect
# be, though it must be able to hook in correctly
# with the NXT library.
def add(port, identifier, klass)
raise TypeError.new('Expected port to be a Symbol') unless port.is_a?(Symbol)
raise TypeError.new('Expected identifier to be a Symbol') unless identifier.is_a?(Symbol)
raise TypeError.new('Expected klass to be a Class') unless klass.is_a?(Class)
assert_in('port', port, PORTS)
assert_responds_to('identifier', identifier, :to_sym)
assert_type('klass', klass, Class)

unless PORTS.include?(port)
raise TypeError.new("Expected port to be one of: :#{PORTS.join(', :')}")
end

port_variable = :"@#{port}"

if !self.respond_to?(identifier)
# Makes a new instance of the class and pushes it into our instance variable
# for the given port.
self.instance_variable_set(port_variable, klass.new(port, self.interface))

# Given that that succeeded, all that remains is to add the identifier
# to our lookup Hash. We'll use this Hash later on within method_missing.
@port_identifiers[identifier] = port

# Define a method on the eigenclass of this instance.
(class << self; self; end).send(:define_method, identifier) do
self.instance_variable_get(port_variable)
end
else
if !self.instance_variable_get(port_variable).nil?
raise PortTakenError.new("Port #{port} is already set, call remove first")
else
if self.respond_to?(identifier)
if self.instance_variable_get(:"@#{port}").nil?
raise InvalidIdentifierError.new("Cannot use identifier #{identifier}, a method on #{self.class} is already using it.")
else
raise PortTakenError.new("Port #{port} is already set, call remove first")
end
end

define_port_handler_method(port, identifier, klass)
end

# Remove the assigned (if any) connector instance from the given
# identifier.
#
# @param Symbol identifier The identifier to search for and remove.
def remove(identifier)
raise TypeError.new('Expected identifier to be a Symbol') unless identifier.is_a?(Symbol)
!!@port_identifiers.delete(identifier)
assert_responds_to('identifier', identifier, :to_sym)
!!@port_identifiers.delete(identifier.to_sym)
end

# This will dynamically add methods like:
Expand All @@ -150,4 +134,23 @@ def remove(identifier)
end
end
end

private

def define_port_handler_method(port, identifier, klass)
port_variable = :"@#{port}"

# Makes a new instance of the class and pushes it into our instance variable
# for the given port.
self.instance_variable_set(port_variable, klass.new(port, self.interface))

# Given that that succeeded, all that remains is to add the identifier
# to our lookup Hash. We'll use this Hash later on within method_missing.
@port_identifiers[identifier.to_sym] = port

# Define a method on the eigenclass of this instance.
(class << self; self; end).send(:define_method, identifier.to_sym) do
self.instance_variable_get(port_variable)
end
end
end
14 changes: 12 additions & 2 deletions lib/nxt/utils/assertions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ module NXT
module Utils
module Assertions
def assert_in(name, value, values)
unless values.include?(value)
raise TypeError.new("Expected #{name} to be one of: :#{values.join(', :')}")
raise TypeError.new("Expected #{name} to be one of: :#{values.join(', :')}") unless values.include?(value)
end

def assert_type(name, value, type)
raise TypeError.new("Expected #{name} to be of type #{type}") unless value.is_a?(type)
end

def assert_responds_to(name, value, *methods)
valid = methods.all? do |method|
value.respond_to?(method)
end

raise TypeError.new("Expected #{name} to respond to: #{methods.join(', ')}") unless valid
end
end
end
Expand Down
79 changes: 41 additions & 38 deletions spec/nxt/nxt_brick_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,8 @@
end

describe '#add' do
it 'should raise an exception if an invalid type of port is given' do
expect do
subject.add('not a symbol', :symbol, Class)
end.to raise_exception(TypeError, 'Expected port to be a Symbol')
end

it 'should raise an exception if an invalid type of identifier is given' do
expect do
subject.add(:symbol, 'not a symbol', Class)
end.to raise_exception(TypeError, 'Expected identifier to be a Symbol')
end

it 'should raise an exception if an invalid type of klass is given' do
expect do
subject.add(:symbol, :symbol, 'not a class')
end.to raise_exception(TypeError, 'Expected klass to be a Class')
before do
subject.stub(:define_port_handler_method)
end

it 'should raise an exception if an invalid port number or letter is given' do
Expand All @@ -102,18 +88,16 @@
end.to raise_exception(TypeError, 'Expected port to be one of: :a, :b, :c, :one, :two, :three, :four')
end

it 'should create a new instance of the passed klass and store it in the attribute for the given port' do
port = :a
class_stub = Class.new
class_return_stub = stub()

class_stub.should_receive(:new) do
class_return_stub
end.with(port, an_instance_of(NXT::Interface::Usb)).once()

subject.add(port, :hello, class_stub)
it 'should raise an exception if an invalid type of identifier is given' do
expect do
subject.add(:a, 123, Class)
end.to raise_exception(TypeError, 'Expected identifier to respond to: to_sym')
end

subject.send(port).should equal(class_return_stub)
it 'should raise an exception if an invalid type of klass is given' do
expect do
subject.add(:a, :symbol, 'not a class')
end.to raise_exception(TypeError, 'Expected klass to be of type Class')
end

it 'should raise an exception if the port given is already set' do
Expand Down Expand Up @@ -142,23 +126,15 @@
end.to raise_error(InvalidIdentifierError, "Cannot use identifier #{identifier}, a method on NXTBrick is already using it.")
end

it 'should set up the port identifiers correctly' do
port = :a
identifier = :hello_world
class_stub = Class.new
class_stub.stub(:new)

subject.add(port, identifier, class_stub)

subject.port_identifiers[identifier].should equal(port)
it 'should call #define_port_handler_method' do
end
end

describe '#remove' do
it 'should raise an exception if an invalid type of identifier is given' do
expect do
subject.remove('not a symbol')
end.to raise_exception(TypeError, 'Expected identifier to be a Symbol')
subject.remove(123)
end.to raise_exception(TypeError, 'Expected identifier to respond to: to_sym')
end

it 'should remove any matching identifiers' do
Expand All @@ -185,4 +161,31 @@
return_value.should be_false
end
end

describe '#define_port_handler_method' do
it 'should create a new instance of the passed klass and store it in the attribute for the given port' do
port = :a
class_stub = Class.new
class_return_stub = stub()

class_stub.should_receive(:new) do
class_return_stub
end.with(port, an_instance_of(NXT::Interface::Usb)).once()

subject.send(:define_port_handler_method, port, :hello, class_stub)

subject.send(port).should equal(class_return_stub)
end

it 'should set up the port identifiers correctly' do
port = :a
identifier = :hello_world
class_stub = Class.new
class_stub.stub(:new)

subject.send(:define_port_handler_method, port, identifier, class_stub)

subject.port_identifiers[identifier].should equal(port)
end
end
end
2 changes: 2 additions & 0 deletions watchr.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
watch('spec/.*_spec\.rb') { |md| system("rspec #{md[0]}") }
watch('lib/(.*)\.rb') { |md| system("rspec spec/#{md[1]}_spec.rb") }

0 comments on commit 92d3481

Please sign in to comment.