Using "should" with class methods? - ruby

I'm used to making calls such as:
new_count.should eql(10)
on variables, but how can I do something similar with a class method such as File.directory?(my_path)?
Every combination of File.should be_directory(my_path) that I've tried leads to a method missing, as Ruby tries to find "be_directory" on my current object, rather than matching it against File.
I know I can turn it around and write
File.directory?(my_path).should == true
but that gives a really poor message when it fails.
Any ideas?

Hmm, maybe I have an idea.
File is part of Ruby proper, so it may have elements written in C. Some of Ruby's meta-programming tools break down when dealing with classes imported from C, that could explain Rspec's failure to make .should behave as expected.
If that's true, there is no real solution here. I'd suggest using the MockFS library:
http://mockfs.rubyforge.org/
This downside to MockFS is using it everywhere you'd normally use File, Dir and FileUtils:
require 'mockfs'
def move_log
MockFS.file_utils.mv( '/var/log/httpd/access_log', '/home/francis/logs/' )
end
The upside, especially if your code is file-intensive, is the ability to spec really complex scenarios out, and have them run without actually touching the slow filesystem. Everything happens in memory. Faster, more complete specs.
Hope this helps, Good luck!

I'm not sure why be_directory wouldn't work for you. What version of rspec are you using? You can also use rspec's predicate_matchers method, when a predicate exists, but it doesn't read nicely as be_predicate.
Here's what I tried:
describe File, "looking for a directory" do
it "should be directory" do
File.should be_directory("foo")
end
predicate_matchers[:find_the_directory_named] = :directory?
it "should find directory" do
File.should find_the_directory_named("foo")
end
end
And that gave me the following output (run with spec -fs spec.rb):
File looking for a directory
- should be directory
- should find directory
Finished in 0.004895 seconds
2 examples, 0 failures

Related

Guard Ruby classes with class-level metaprogramming against multiple loads

A quirk of Ruby's require is that, while in general, it will only load a file once, if that file is accessible via multiple paths (e.g. symlinks), it can be required multiple times. This causes problems when there are things like class-level metaprogramming, or in general a code that should only be executed once on file loading, getting executed multiple times.
Is there any way, from inside a Ruby class definition, to tell whether the class has been defined before? I thought defined? or Object.const_get might tell me, but from those it looks like the class is defined as soon as it's opened.
This is not an answer to your question in the second paragraph, but a solution to the issue in your first paragraph. Actually, you cannot avoid multiple file loads by checking whether a class was defined already.
Instead of doing:
require some_file_name
do:
require File.realpath(some_file_name)
By doing so, different symbolic links pointing to the same real file would be normalized to the same real file name, and hence multiple loading of them would be correctly filtered by require.
Cf. this question and the answer given there.
The real solution is that requiring that a piece of code is only executed once is bad design and you should fix that.
However, what you could do is simply set some flag that the code has already been executed and check that flag. E.g.:
class Foo
unless #__executed__
def bla; end
puts 'Test'
end
#__executed__ = true
end

How to iterate for lazy compilation

How in Ruby do I ...
iterate over two folders (paths given as an initial constant or paths taken from a json file)
and compare their time stamps, triggering a certain command (an empty foo()-function for now, just printing the filename) on the former, if it's timestamp is same-or-old than the latter.
I want to build a lazy aka lean update mechanism, much like make in the old days, which only re-compiled C files, if they are newer.
[ And that, just to let you know, I want to do, to bypass some heavy corporate JEE deployment mechanism, where in fact only one or two css/jsp files keep changing (as frontend designers do) and need to be pushed from dev to server root. :-]
Forgive me the scope of the question, I am a complete noob to file operations in ruby... but it seems the best available cross-plattform scripting in my environment at hand.
def foo(bar)
puts bar
end
def bar(first,second)
puts "#{first} is not older than #{second}"
end
def compare_time_stamps
Dir.foreach("path/one") do |filename|
#first_file << File.ctime(filename)
end
Dir.foreach("path/two") do |filename|
#second_file << File.ctime(filename)
end
#first_file.zip(#second_file)
#first_file.each do |first_time,second_time|
#first_time.parse_time >= #second_time.parse_time ? foo(#first_time) : bar(#first_time,#second_time)
end
end
edit--
Which time stamps are you trying to compare #Fronker? Are you trying to compare file1 of folder 1 with file1 of folder 2 or file1 of folder 1 with file 2 of folder 1
This is not tested, but with some editing it should work. You will need to make a method -- perhaps using regex -- to parse the 'ctime' method into a time stamp format that you want. This is really rough and i'm not for sure if it will even work like it is supposed to so take it lightly with a grain of salt. When I get home I will edit it to be better but for now this should get you started with which methods might work.
Hopfully this helps somewhat, Happy coding!
Actually, there is a build system written in Ruby, called rake (stems from Ruby Make) that already has an implementation of detecting changed task dependencies and only running a chain of tasks that are affected.
You might find this introduction to rake useful.

What strategy should I use to unit test reading a file in ruby (rspec)?

I've got a ruby method which processes a very large file line-by-line (ie I cannot load the file into memory) like the following:
def process_file
file = File.new(##data_file, "r")
while (line = file.gets)
{ do something with the line } ...
end
file.close
return "upload complete"
end
As per best practices, I do not want this this method to have any reliance on the filesystem. I've used stubs before, but I'm not sure how I would go about using it here. I would imagine I would use something equivalent to:
file = File.stub!(:new).and_return({something})
I'm just not sure what {something} is.
I am using rspect and Any assistance would be appreciated. Thanks.
You could always check if you call proper methods, at least the close() after. I think that's the most often error with i/o.
You might want to look at MockFS, which mocks out the operation of the filesystem.
I don't think that a unit test is called for here. Are you afraid that, somehow, creating files/closing files is going to be broken in some future version of Ruby? It's not, don't worry.
Let the Ruby devs worry about unit testing their platform specific file IO routines. You should worry about unit testing your algorithms, and specifically those algorithms which are critical and complex (i.e., prone to breakage if you're not careful).
That said, you should probably just pass a block to the constructor. This ensures that the file will be closed for you when the block exits and is more idiomatic.
File.open(some_file_path, 'w+') do |file|
file.puts('content')
end

Adding a directory to $LOAD_PATH (Ruby)

I have seen two commonly used techniques for adding the directory of the file currently being executed to the $LOAD_PATH (or $:). I see the advantages of doing this in case you're not working with a gem. One seems more verbose than the other, obviously, but is there a reason to go with one over the other?
The first, verbose method (could be overkill):
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
and the more straightforward, quick-and-dirty:
$:.unshift File.dirname(__FILE__)
Any reason to go with one over the other?
The Ruby load path is very commonly seen written as $: , but just because it is short, does not make it better. If you prefer clarity to cleverness, or if brevity for its own sake makes you itchy, you needn't do it just because everyone else is.
Say hello to ...
$LOAD_PATH
... and say goodbye to ...
# I don't quite understand what this is doing...
$:
I would say go with $:.unshift File.dirname(__FILE__) over the other one, simply because I've seen much more usage of it in code than the $LOAD_PATH one, and it's shorter too!
I'm not too fond on the 'quick-and-dirty' way.
Anyone new to Ruby will be pondering what $:. is.
I find this more obvious.
libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
Or if I care about having the full path...
libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
UPDATE 2009/09/10
As of late I've been doing the following:
$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
I've seen it in a whole bunch of different ruby projects while browsing GitHub.
Seems to be the convention?
If you type script/console in your Rails project and enter $:, you'll get an array that includes all the directories needed to load Ruby. The take-away from this little exercise is that $: is an array. That being so, you can perform functions on it like prepending other directories with the unshift method or the << operator. As you implied in your statement $: and $LOAD_PATH are the same.
The disadvantage with doing it the quick and dirty way as you mentioned is this: if you already have the directory in your boot path, it will repeat itself.
Example:
I have a plugin I created called todo. My directory is structured like so:
/---vendor
|
|---/plugins
|
|---/todo
|
|---/lib
|
|---/app
|
|---/models
|---/controllers
|
|---/rails
|
|---init.rb
In the init.rb file I entered the following code:
## In vendor/plugins/todo/rails/init.rb
%w{ models controllers models }.each do |dir|
path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
$LOAD_PATH << path
ActiveSupport::Dependencies.load_paths << path
ActiveSupport::Dependencies.load_once_paths.delete(path)
end
Note how I tell the code block to perform the actions inside the block to the strings 'models', 'controllers', and 'models', where I repeat 'models'. (FYI, %w{ ... } is just another way to tell Ruby to hold an array of strings). When I run script/console, I type the following:
>> puts $:
And I type this so that it is easier to read the contents in the string. The output I get is:
...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
As you can see, though this is as simple an example I could create while using a project I'm currently working on, if you're not careful the quick and dirty way will lead to repeated paths. The longer way will check for repeated paths and make sure they don't occur.
If you're an experienced Rails programmer, you probably have a very good idea of what you're doing and likely not make the mistake of repeating paths. If you're a newbie, I would go with the longer way until you understand really what you're doing.
Best I have come across for adding a dir via relative path when using Rspec. I find it verbose enough but also still a nice one liner.
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
There is a gem which will let you setup your load path with nicer and cleaner code. Check this out: https://github.com/nayyara-samuel/load-path.
It also has good documentation
My 2ยข: I like $LOAD_PATH rather than $:. I'm getting old... I've studied 92,000 languages. I find it hard to keep track of all the customs and idioms.
I've come to abhor namespace pollution.
Last, when I deal with paths, I always delete and then either append or prepend -- depending upon how I want the search to proceed. Thus, I do:
1.times do
models_dir = "#{File.expand_path(File.dirname(__FILE__))}/models"
$LOAD_PATH.delete(models_dir)
$LOAD_PATH.unshift(models_dir)
end
I know it's been a long time since this question was first asked, but I have an additional answer that I want to share.
I have several Ruby applications that were developed by another programmer over several years, and they re-use the same classes in the different applications although they might access the same database. Since this violates the DRY rule, I decided to create a class library to be shared by all of the Ruby applications. I could have put it in the main Ruby library, but that would hide custom code in the common codebase which I didn't want to do.
I had a problem where I had a name conflict between an already defined name "profile.rb", and a class I was using. This conflict wasn't a problem until I tried to create the common code library. Normally, Ruby searches application locations first, then goes to the $LOAD_PATH locations.
The application_controller.rb could not find the class I created, and threw an error on the original definition because it is not a class. Since I removed the class definition from the app/models section of the application, Ruby could not find it there and went looking for it in the Ruby paths.
So, I modified the $LOAD_PATH variable to include a path to the library directory I was using. This can be done in the environment.rb file at initialization time.
Even with the new directory added to the search path, Ruby was throwing an error because it was preferentially taking the system-defined file first. The search path in the $LOAD_PATH variable preferentially searches the Ruby paths first.
So, I needed to change the search order so that Ruby found the class in my common library before it searched the built-in libraries.
This code did it in the environment.rb file:
Rails::Initializer.run do |config|
* * * * *
path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)
* * * * *
end
I don't think you can use any of the advanced coding constructs given before at this level, but it works just fine if you want to setup something at initialization time in your app. You must maintain the original order of the original $LOAD_PATH variable when it is added back to the new variable otherwise some of the main Ruby classes get lost.
In the application_controller.rb file, I simply use a
require 'profile'
require 'etc' #etc
and this loads the custom library files for the entire application, i.e., I don't have to use require commands in every controller.
For me, this was the solution I was looking for, and I thought I would add it to this answer to pass the information along.

Passing a parameter/object to a ruby unit/test before running it using TestRunner

I'm building a tool that automates a process then runs some tests on it's own results then goes to do some other stuff.
In trying to clean up my code I have created a separate file that just has the test cases class. Now before I can run these tests, I have to pass the class a couple of parameters/objects before they can be run. Now the problem is that I can't seem to find a way to pass a parameter/object to the test class.
Right now I am thinking to generate a Yaml file and read it in the test class but it feels "wrong" to use a temporary file for this. If anyone has a nicer solution that would be great!
**************Edit************
Example Code of what I am doing right now:
#!/usr/bin/ruby
require 'test/unit/ui/console/testrunner'
require 'yaml'
require 'TS_SampleTestSuite'
automatingSomething()
importantInfo = getImportantInfo()
File.open('filename.yml', 'w') do |f|
f.puts importantInfo.to_yaml
end
Test::Unit::UI::Console::TestRunner.run(TS_SampleTestSuite)
Now in the example above TS_SampleTestSuite needs importantInfo, so the first "test case" is a method that just reads in the information from the Yaml file filname.yml.
I hope that clears up some confusion.
Overall, it looks like you're not really using the unit tests in a very rubyish way, but I'll leave that aside for a minute.
Your basic problem is that you have some setup that needs to happen before the tests run. The normal way to do that is with a setup method within the test unit case itself.
class UserTest < TestUnit::TestCase
def setup
# do your important calculation
end
def test_success
#.. assert some things
end
end
I would give some thought to what code it is that you're actually testing here, and see if you can break it down and test it in a more granular way, with lots more tests.
First, I agree with Cameron, this code definitely does not adhere to the Ruby way, though I'll also sidestep that for now.
The fastest way to get up and running with this, especially if this data is pretty much immutable (that is to say, your tests won't be altering it in anyway), is to just assign the value to a constant. So instead of naming your variable importantInfo, you name it IMPORTANT_INFO. Then it will be available to you in your tests. It's definitely not a pretty solution, and I think it couuld even be considered a test smell that you need that sort of global setup, but it's there for you.
Alternatively, you could look at stubbing out the importantInfo, which I actually think would provide for much cleaner and more readable tests.

Resources