How to write a Ruby UnitTest for a class- or module-level constant? - ruby

How does one write a test to verify a constant is present in a class or module?
Example:
module MyModule
VERSION = "0.3.1"
end
I tried
require 'test/unit'
require 'shoulda'
require "my_class"
class MyModuleTest < Test::Unit::TestCase
should "have a Version constant" do
# next two lines crash
assert_respond_to MyModule, :VERSION
assert_respond_to 'VERSION', MyModule
end
end

Would you consider to use Specular for more natural workflow, meant you can use any method that works in plain Ruby, thus you do not need to remember lot of extra unneeded stuff.
require 'specular'
module MyModule
VERSION = "0.3.1"
end
Spec.new do
check(MyModule).const_defined? :VERSION
end
puts Specular.run
# => check(MyModule).const_defined? :VERSION
# => - passed
# => Specs: 1
# => Tests: 0
# => Assertions: 1
So with plain Ruby you do: MyModule.const_defined? :VERSION
and with Specular: check(MyModule).const_defined? :VERSION
not really big difference thus nothing to learn/remember/recall again and again.

Related

Use Geocoder with Sinatra and DataMapper

I'm attempting to use the Geocoder gem with a DataMapper model in a Sinatra application.
environment.rb:
require 'rubygems'
require 'bundler/setup'
require 'dm-core'
require 'dm-timestamps'
require 'dm-validations'
require 'dm-aggregates'
require 'dm-migrations'
require 'dm-types'
require 'geocoder'
require 'sinatra' unless defined?(Sinatra)
# load models
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
Dir.glob("#{File.dirname(__FILE__)}/lib/*.rb") { |lib| require File.basename(lib, '.*') }
DataMapper.setup(:default, (ENV["DATABASE_URL"] || "sqlite3:///#{File.expand_path(File.dirname(__FILE__))}/#{Sinatra::Base.environment}.db"))
DataMapper.finalize
DataMapper.auto_upgrade!
lib/location.rb:
class Location
include DataMapper::Resource
include Geocoder::Model::Base
property :id, Serial
property :address, String, :required => true
# geocoder gem
geocoded_by :address, :latitude => :lat, :longitude => :lng
# geocoder
after_validation :geocode, :if => :address_changed?
end
When I attempt to start an IRB session, an exception is generated:
irb> require './environment'
NameError: uninitialized constant Geocoder::Model
...
What am I not understanding?
First up, it looks like the Geocode gem won't have direct support for Datamapper, as per this issue.
Second, when you include a module inside a class, the methods are available to the instance of the class, and not at the class level. For example:
module Name
def name
puts "Module"
end
end
class SomeClass
include Name
end
SomeClass.new.name # => "Module"
This works because when you include a module, that module gets added to the ancestor chain of that class. Any methods that get sent to the instance which are not available on the instance are forwarded to the ancestors. However, there's another method called extend which adds the methods at a class-level rather than at instance level:
# module definition same as before
class SomeClass
extend Name
name # works!
end
Inorder to get class-level inclusion, there is another way (which is what the Geocoder gem uses for supported models:
# module code same as before
module Name
def name
puts "Module"
end
def self.included(klass)
klass.extend(self)
end
end
The included hook is provided for models which can be overridden to do something when the include Name step executes. Since there's no Datamapper specific module that is not executing this step, you see that error.

Why do I get the error uninitialized constant Stuff::HTTParty?

I have the HTTParty gem on my system and I can use it from within rails.
Now I want to use it standalone.
I am trying:
class Stuff
include HTTParty
def self.y
HTTParty.get('http://www.google.com')
end
end
Stuff.y
but I get
$ ruby test_httparty.rb
test_httparty.rb:2:in `<class:Stuff>': uninitialized constant Stuff::HTTParty (NameError)
from test_httparty.rb:1:in `<main>'
07:46:52 durrantm Castle2012 /home/durrantm/Dropnot/_/rails_apps/linker 73845718_get_method
$
You have to require 'httparty':
require 'httparty'
class Stuff
include HTTParty
# ...
end
Its all because of the include which exists with in the class
If you include a class with a module, that means you're "bringing in" the module's methods as instance methods.
If you need more clarity on include and require
I request you to refer to this wonderful SO Posting
What is the difference between include and require in Ruby?
Here is an example which I have taken from the same posting
module A
def say
puts "this is module A"
end
end
class B
include A
end
class C
extend A
end
B.say => undefined method 'say' for B:Class
B.new.say => this is module A
C.say => this is module A
C.new.say => undefined method 'say' for C:Class

Dynamically loading Thor options for a Ruby Gem

While trying to develop a simple gem to learn the process, I happened to stumble on this issue: Thor DSL takes in options to a command using the syntax: option :some_option, :type => :boolean, just prior to the method definition.
I am trying to have a dynamic set of options loaded from a file. I do this file read operation in the constructor, but it seems the option keyword for the Thor class is getting processed before the initialize method.
Any ideas to resolve this? Also it would be great if someone can explain how the option keyword works? I mean is option a method call? I don't get the design. (This is the first DSL I am trying out and am a total newbie to Ruby Gems)
#!/usr/bin/env ruby
require 'thor'
require 'yaml'
require 'tinynews'
class TinyNewsCLI < Thor
attr_reader :sources
#sources = {}
def initialize *args
super
f = File.open( "sources.yml", "r" ).read
#sources = YAML::load( f )
end
desc "list", "Lists the available news feeds."
def list
puts "List of news feed sources: "
#sources.each do |symbol, source|
puts "- #{source[:title]}"
end
end
desc "show --source SOURCE", "Show news from SOURCE feed"
option :source, :required => true
def show
if options[:source]
TinyNews.print_to_cli( options[:source].to_sym )
end
end
desc "tinynews --NEWS_SOURCE", "Show news for NEWS_SOURCE"
#sources.keys.each do |source_symbol| # ERROR: States that #sources.keys is nil
#[:hindu, :cnn, :bbc].each do |source_symbol| # I expected the above to work like this
option source_symbol, :type => :boolean
end
def news_from_option
p #sources.keys
TinyNews.print_to_cli( options.keys.last.to_sym )
end
default_task :news_from_option
end
TinyNewsCLI.start( ARGV )
After a bit of tweaking, I think I ended upon a solution that doesn't look too bad. But not sure placing code in module like that is a good practice. But anyways:
#!/usr/bin/env ruby
require 'thor'
require 'yaml'
require 'tinynews'
module TinyNews
# ***** SOLUTION *******
f = File.open( "../sources.yml", "r" ).read
SOURCES = YAML::load( f )
class TinyNewsCLI < Thor
default_task :news_from_source
desc "list", "Lists the available news feeds."
def list
puts "List of news feed sources: "
SOURCES.each do |symbol, source|
puts "- #{source[:title]}"
end
end
desc "--source NEWS_SOURCE", "Show news for NEWS_SOURCE"
option :source, :required => true, :aliases => :s
def news_from_source
TinyNews.print_to_cli( options[:source].to_sym )
end
end
end
TinyNews::TinyNewsCLI.start( ARGV )

uninitialized constant BikeShare (NameError)

I'm trying to implement some simple testing in rspec for a gem I'm writing. When I comment out describe BikeShare do down to end and run the file, the file loads in and runs successfully. I'm sure it's something tiny I'm missing.
My test file is really simple and looks like this:
require 'spec_helper'
describe BikeShare do
it "should run" do
# response = BikeShare.new
# response.should_be present
end
end
When run, I get the error uninitialized constant BikeShare (NameError) at line 3.
My bikeshare.rb file looks like this, fairly simple:
class BikeShare
def initialize
response = JSON.parse(open("http://bayareabikeshare.com/stations/json").read)
#response = response["stationBeanList"]
end
def get_last_station
#response.last["id"]
end
end
My Rakefile looks like this:
require 'rubygems'
require 'bundler'
Bundler.setup
Bundler::GemHelper.install_tasks
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new do |spec|
# spec.libs << 'lib' << 'spec'
spec.pattern = 'spec/*_spec.rb'
end
task :default => :spec
Your tests arent aware of BikeShare.
You need to require the file that defines your BikeShare class. I dont use rspec but I think that you normally set up your testing environment in spec_helper.rb.

cattr_accessor outside of rails

I'm trying to use the google_search ruby library (code follows) but it complains that 'cattr_accessor is an undefined method' - any ideas why this might be or how I could fix it?
require 'rubygems'
require 'google_search'
GoogleSearch.web :q => "pink floyd"
cattr_accessor seems to be a Rails extension that acts like attr_accessor, but is accessible on both the class and its instances.
If you want to copy the source of the cattr_accessor method, check out this documentation:
# File vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb, line 46
def cattr_accessor(*syms)
cattr_reader(*syms)
cattr_writer(*syms)
end
# File vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb, line 4
def cattr_reader(*syms)
syms.flatten.each do |sym|
next if sym.is_a?(Hash)
class_eval("unless defined? ##\#{sym}\n##\#{sym} = nil\nend\n\ndef self.\#{sym}\n##\#{sym}\nend\n\ndef \#{sym}\n##\#{sym}\nend\n", __FILE__, __LINE__)
end
end
# File vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb, line 24
def cattr_writer(*syms)
options = syms.extract_options!
syms.flatten.each do |sym|
class_eval("unless defined? ##\#{sym}\n##\#{sym} = nil\nend\n\ndef self.\#{sym}=(obj)\n##\#{sym} = obj\nend\n\n\#{\"\ndef \#{sym}=(obj)\n##\#{sym} = obj\nend\n\" unless options[:instance_writer] == false }\n", __FILE__, __LINE__)
end
end
You can get this functionality by including the Ruby Facets gem. Reference the source here:
https://github.com/rubyworks/facets/blob/master/lib/core/facets/cattr.rb
You generally don't need to require all code from the gem. You can selectively require what you want. There are quite a few useful extensions in the gem though.

Resources