{ |one, step, back| } 46 to 55 of 193 articles Syndicate: full/short

FlexMock 0.6.0 Released   17 Apr 07
[ print link all ]
FlexMock version 0.6.0 was just released over the weekend. You can read the release announcement below for all the new features and enhancements.

FlexMock is a flexible mocking library for use in unit testing and behavior specification in Ruby. Version 0.6.0 introduces a number of API enhancements to make testing with mocks even easier than before.

New in 0.6.0

  • Better integration with Test::Unit (no need to explicitly include FlexMock::TestCase).
  • Integration with RSpec (version 0.9.0 or later of RSpec is required).
  • The flexmock method will now create both regular mocks and partial mocks.
       flexmock()          # => a full mock
       flexmock(person)    # => a partial mock based on person
    

    (flexstub is still included for backwards compatibility).

  • Quick and simple mocks my now be created using an expectation hash. For example:
      flexmock(:foo => 10, :bar => "Hello")
    

    will create a mock with two methods, :foo and :bar,defined. :foo will return 10 when invoked, and :bar will return "Hello".

  • The should_receive method will now allow multiple methods (with the same constraints) be defined in a single call. For example, the following declares that both :read and :write need to be called at least one time each on the mock object.
      flexmock.should_receive(:read, :write).at_least.once
    
  • should_recieve now will allow expectation hashes as arguments. This is similar to the list of methods, but allows each defined method to have its own return value.
      flexmock.should_receive(:name => "John", :age => 32)
    
  • In addition to using a block for defining constrains, constraints may now be applied directly to the return value of new_instances. Combined with the expectation hashes supported by should_receive, simple mocking scenarios have become much more succinct. For example:
        flexmock(Person).new_instances.should_receive(:name => "John", :age => 32)
    
  • Improved implementation, allowing for more flexible use and greater consistency between full mock and partial mocks.
  • Version 0.6.0 also includes a fix for an incompatibility with some older versions of RCov. The FlexMock Rakefile now includes a RCov task (and we have 100% code coverage).

What is FlexMock?

FlexMock is a flexible framework for creating mock object for testing. When running unit tests, it is often desirable to use isolate the objects being tested from the "real world" by having them interact with simplified test objects. Sometimes these test objects simply return values when called, other times they verify that certain methods were called with particular arguments in a particular order.

FlexMock makes creating these test objects easy.

Features

  • Easy integration with both Test::Unit and RSpec. Mocks created with the flexmock method are automatically verified at the end of the test or example.
  • A fluent interface that allows mock behavior to be specified very easily.
  • A "record mode" where an existing implementation can record its interaction with a mock for later validation against a new implementation.
  • Easy mocking of individual methods in existing, non-mock objects.
  • The ability to cause classes to instantiate test instances (instead of real instances) for the duration of a test.

Example

Suppose you had a Dog object that wagged a tail when it was happy. Something like this:

  class Dog
    def initialize(a_tail)
      @tail = a_tail
    end
    def happy
      @tail.wag
    end
  end

To test the Dog class without a real Tail object (perhaps because real Tail objects activate servos in some robotic equipment), you can do something like this:

require ‘test/unit’ require ‘flexmock/test_unit‘

  class TestDog < Test::Unit::TestCase
    def test_dog_wags_tail_when_happy
      tail = flexmock("tail")
      tail.should_receive(:wag).once
      dog = Dog.new(tail)
      dog.happy
    end
  end

FlexMock will automatically verify that the mocked tail object received the message wag exactly one time. If it doesn’t, the test will not pass.

See the FlexMock documentation at flexmock.rubyforge.org for details on specifying arguments and return values on mocked methods, as well as a simple technique for mocking tail objects when the Dog class creates the tail objects directly.

Availability

You can make sure you have the latest version with a quick RubyGems command:

  gem install flexmock    (you may need root/admin privileges)

Otherwise, you can get it from the more traditional places:

Download:rubyforge.org/project/showfiles.php?group_id=170

You will find documentation at: flexmock.rubyforge.org.

— Jim Weirich


comments

Extended FlexMock Example Using Google4R   15 Apr 07
[ print link all ]
I recently helped a friend use FlexMock to do some testing on code that was written ot use the Google4R checkout APIs. I thought it might be interesting to share some of the details here. Note that this code uses the recently released FlexMock version 0.6.0.

Google4R is a simple Ruby wrapper around the Google APIs. In this extended example, we will use FlexMock to test software that uses the Google APIs, without every communicating with Google itself.

Purchase.rb

Here is the bit of code that we will be testing…

  require 'google4r/checkout'
  require 'item'

  class Purchase

    def initialize(config)
      @frontend = Frontend.new(config)
      @frontend.tax_table_factory = TaxTableFactory.new
    end

    # Purchase the +quantity+ items identified by +item_id+.  Return the
    # confirmation page URL.
    def purchase(item_id, quantity=1)
      item = Item.find(item_id)
      checkout = @frontend.create_checkout_command
      checkout.cart.create_item do |cart_item|
        cart_item.name = item.name
        cart_item.description = item.description
        cart_item.unit_price = item.unit_price
        cart_item.quantity = quantity
      end
      response = checkout.send_to_google_checkout
      response.redirect_url
    end

  end

FrontEnd is a Google4R class that provides a lot of the front end work for talking to the Google APIs. The config object given to the Purchase initializer is simply a hash of values defining the merchant_id, merchant_key and sandbox flag. To use the real Google checkout APIs, you will need to obtains a merchant id and key from Google. Since we will be mocking the Google interaction, we can use dummy values in our test.

The tax table factory is required by the Google4R software. We provide the following simplified one. Read the Google API documents for more information.

  class TestTaxTableFactory
    def effective_tax_tables_at(time)
      tax_free_table = TaxTable.new(false)
      tax_free_table.name = "default table"
      tax_free_table.create_rule do |rule|
        rule.area = UsCountryArea.new(UsCountryArea::ALL)
        rule.rate = 0.0
      end
      return [tax_free_table]
    end
  end

Item is simply an ActiveRecord class that we are using to hold our purchase item information. It should respond to the name, description and unit_price messages.

Testing Without Using External Resources

Our first test attempt will be to run the purchase method without talking to either the live Google web services, or hitting an actual ActiveRecord database.

Mocking Active Record

The ActiveRecord part is easy to mock. The following will handle it:

  flexmock(Item).should_receive(:find).with(1).and_return(
    flexmock("guitar",
      :name => "Deschutes",
      :description => "Deschutes model Guitar",
      :unit_price => Money.new(2400.00)))

We have mocked out the find method on Item so that whenever we call find with an integer argument of 1, we will return a mock item that will report its name, description and unit_price. This gives us an item for testing without actually reading the database.

Mocking the Google Web Services Call

Next we want to prevent the Google4R API from actually talking to the live web service. Everything that happens in the purchase method is all done locally except for the final call to send_to_google_checkout. All we need to do is mock out that one method.

  flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
    instance.should_receive(:send_to_google_checkout).once.
      and_return(flexmock(:redirect_url => "http://google.response.url"))
  end

When we ask FrontEnd to create a check out command, it returns an instance of Google4R::Checkout::CheckoutCommand. We then use flexmock to specify that when Google4R::Checkout::CheckoutCommand creates a new instance, it should actually return a partial mock of that instance. The block given to the new_instances method allows us to configure the mocked checkout command. We tell it return a response object (yes, another mock) that report our dummy response URL.

The Final Result

Here is the complete unit test:

  def test_buying_a_guitar
    # Setup
    flexmock(Item).should_receive(:find).with(1).and_return(
      flexmock("guitar",
        :name => "Deschutes",
        :description => "Deschutes model Guitar",
        :unit_price => Money.new(2400.00)))

    flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
      instance.should_receive(:send_to_google_checkout).once.
        and_return(flexmock(:redirect_url => "http://google.response.url"))
    end

    # Execute
    p = Purchase.new({
      :merchant_id => 'dummy_id',
      :merchant_key => 'dummy_key',
      :use_sandbox => true })
    url = p.purchase(1)

    # Assert
    assert_equal "http://google.response.url", url
  end

Testing the Details

The above test is fine as far as it goes. It demonstrates how to use mocks to avoid talking to external resources such as databases and web services. But as a unit test, it is sorely lacking in several areas.

All the test really demonstrates is that the send_to_google_checkout method is called. There are no tests to ensure that the right item descriptions and prices are correctly stored in the cart. In fact, if we rewrote the purchase method as follows:

  def purchase(item_id, quantity=1)
    @frontend.create_checkout_command.send_to_google_checkout.redirect_url
  end

it would still pass the unit test we designed, even though the rewrite is obviously an incorrect implementation.

A more complete test is a bit more complicated. Here are the details.

Mocking Active Record

Our incorrect version of purchase never calls the find method of Item. We can easily test for that by adding a once constraint one that mock specification. Since find is a read-only method, we don’t really care if it is called multiple times, as long as it is called at least one time, so we will add an at_least modifier as well.

Finally, we are going to break the guitar mock out into its own declaration. The reason will become obvious in a bit.

  mock_guitar = flexmock("guitar",
    :name => "Deschutes",
    :description => "Deschutes model guitar",
    :unit_price => Money.new(2400.00))

  flexmock(Item).should_receive(:find).with(1).at_least.once.
    and_return(mock_guitar)

Mocking a Cart Item

The next bit is a wee bit complicated, but we will handle it a little bit at a time so that it doesn’t become overwhelming.

There are three main objects in the Google checkout API that we deal with in the next section.: (1) the checkout command object returned by the front end, (2) the cart object returned by the checkout command, and (3) the item passed to the block in the create_item call.

We will tackle them in reverse order, starting with the item objects given to the create_item block. The item must respond to four attribute assignments. This is straightforward to mock, just make sure you include the once constraint so that the assignments are required.

  mock_item = flexmock("item")
  mock_item.should_receive(:name=).with(mock_guitar.name).once
  mock_item.should_receive(:description=).with(mock_guitar.description).once
  mock_item.should_receive(:unit_price=).with(mock_guitar.unit_price).once
  mock_item.should_receive(:quantity=).with(1).once

Notice how we used the mock_guitar object defined earlier to provide values in the with constraint. This way we don’t have to repeat the explicit strings and values we are checking. (Keep it DRY!).

Mocking the Cart

The mock cart object will pass the mock_item to a block when the create_item method is called. We specify that with the following:

  mock_cart = flexmock("cart")
  mock_cart.should_receive(:create_item).with(Proc).once.and_return { |block|
    block.call(mock_item)
  }

FlexMock objects can handle blocks passed to them by treating them as the final object in the calling list. Use Proc in the with constraint to match the block and then invoke the block explicitly via block.call(…) in the and_return specification.

Mocking the Checkout Command

Finally, we tie it all together by mocking the checkout command. As before, we use new_instances to force newly created checkout commands to be stubbed. This time we not only mockout the send_to_google method, but we also mock the cart command to return the carefully crafted mock_cart object from the previous section.

  flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
    instance.should_receive(:cart).with().once.and_return(mock_cart)
    instance.should_receive(:send_to_google_checkout).once.
      and_return(flexmock(:redirect_url => "http://google.response.url"))
  end

The Final Test Method

Here is the complete detailed version of the test method.

  def test_buying_a_guitar_with_details
    # Setup
    mock_guitar = flexmock("guitar",
      :name => "Deschutes",
      :description => "Deschutes model guitar",
      :unit_price => Money.new(2400.00))

    flexmock(Item).should_receive(:find).with(1).at_least.once.
      and_return(mock_guitar)

    mock_item = flexmock("item")
    mock_item.should_receive(:name=).with(mock_guitar.name).once
    mock_item.should_receive(:description=).with(mock_guitar.description).once
    mock_item.should_receive(:unit_price=).with(mock_guitar.unit_price).once
    mock_item.should_receive(:quantity=).with(1).once

    mock_cart = flexmock("cart")
    mock_cart.should_receive(:create_item).with(Proc).once.and_return { |block|
      block.call(mock_item)
    }

    flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
      instance.should_receive(:cart).with().once.and_return(mock_cart)
      instance.should_receive(:send_to_google_checkout).once.
        and_return(flexmock(:redirect_url => "http://google.response.url"))
    end

    # Execute
    p = Purchase.new({
      :merchant_id => 'dummy_id',
      :merchant_key => 'dummy_key',
      :use_sandbox => true })
    url = p.purchase(1)

    # Assert
    assert_equal "http://google.response.url", url
  end

Summary

Testing with mock objects can get complex. We used seven different mock or partial mock objects in testing the interaction of our code with the Google checkout API. Most testing scenarios won’t require that many, but anytime your code touches something external, it might require a mock object for testing.

We should stop and ask ourselves: was it worth it? It seems like an awful lot of work just to test a very simple purchase method. Wouldn’t it just be easier to just use the Google API directly for testing and forget about the mocks?

Perhaps, but using mock objects have several definite advantages:

  • You can run the test at any time without worrying whether Google, the internet, or anything else is up and connected.
  • You can easy test for error conditions using mock objects. For example, does your code correctly handle the case where you get an exception when connecting to google? Mocks can easily create those error conditions that are difficult to achieve with real objects.

    E.g.

       instance.should_receive(:send_to_google_checkout).once.
         and_return { raise Google4R::Checkout::GoogleCheckoutError }
    

Some might point out that in the final test method we are hardly using Google4R software at all, most of the code we interact with are mock objects. Doesn’t that defeat the purpose of testing?

The answer is simple. Always keep in mind what you are testing. The goal of the TestPurchase test case is not the make sure the Google4R code is correct, but that our Purchase class correctly interoperates with it. We do that by carefully stating what methods are called with what arguments and what they return. The test just checks that we are using to external software as we expect it to. We don’t actually care about the Google4R software itself in this test case (presumably we do have tests that cover Google4R, but those are different tests).

In the end, mock objects are a power tool to have in your testing toolbox.


comments

Multicasting in Ruby   30 Nov 06
[ print link all ]

It’s a bit hard to dig up, but I did figure out how to do UDP packet multicasting in Ruby.

Multicasting

I’ve been playing around with some network peer to peer discovery techniques and wanted to try some of them out in Ruby. The trick to self discovery is the ability to send network packets without knowing the address of the receiver. To do this, you either have to broadcast or multicast.

Because broadcasted packets are received by every device on the local network, it is generally considered good manners to not use broadcasted packets.

Multicasting, on the other hand, is only received by hosts that explicitly express an interest in the multicast address. Although abuse of multicasting is still bad manners, it is a better option than broadcasting.

Sending Multicast Packets

The IP addresses 224.0.0.0 through 239.255.255.255 are reserved for multicast messages. Simply sending a UDP messsage to an address in that range is sufficient.

One minor refinement is to set the TTL (Time To Live) option on the socket. My understanding is that this controls how far the packet is propagated. As polite net-citizens, we don’t want our multicast packets travelling too far abroad, so we set the TTL value to 1 (we need the ugly packing stuff because the value is passed directly to the C level setsockopt() function with no interpretation by Ruby).

So the Ruby code to send a multicast message is:

require 'socket'
MULTICAST_ADDR = "225.4.5.6" 
PORT= 5000
begin
  socket = UDPSocket.open
  socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, [1].pack('i'))
  socket.send(ARGV.join(' '), 0, MULTICAST_ADDR, PORT)
ensure
  socket.close 
end

Receiving Multicast Messages

Receiving a multicast message is a bit tricker. Since multicast messages are generally ignored unless someone has explicitly registered an interest in a particular address, there is a bit of setup that needs to be done.

Here’s the code for receiving multicast messages:

require 'socket'
require 'ipaddr'
MULTICAST_ADDR = "225.4.5.6" 
PORT = 5000
ip =  IPAddr.new(MULTICAST_ADDR).hton + IPAddr.new("0.0.0.0").hton
sock = UDPSocket.new
sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ip)
sock.bind(Socket::INADDR_ANY, PORT)
loop do
  msg, info = sock.recvfrom(1024)
  puts "MSG: #{msg} from #{info[2]} (#{info[3]})/#{info[1]} len #{msg.size}" 
end

The tricky part was figuring out the right setsockopt options and values needed to register interest in our multicast address. I had to do a little reading in the Unix man pages on the C level setsockopt() function call. The third option to the C function is a structure that contains two 4-byte IP addresses. The first IP address is the multicast address, and the second IP address is the address of the local host adapter that we wish to use to listen for the multicast. The 0.0.0.0 address means use any of the local network adapters. IPAddr handles parsing the human readable form of the IP address and returns a string of 4 bytes in the order needed by the C level setsockopt() function.

Usage

Save the above code in files named send.rb and rcv.rb. In one console window, type:

ruby rcv.rb

In another console window on the same or different machine (on the same local network), type:

ruby send.rb This is a test.

For more fun, bring up several receive windows and all will receive the messages send by the send script.

I can think of all kinds of fun things to do with this.

Update

I added the Time To Live option on send.


comments

Using Rake as a Library -- Update   31 Aug 06
[ print link all ]

Why Not Find?

Why Not Find?

After posting FindInCode, someone legitimately asks: “Why not just use Find?”

Good question! Find is a Ruby supplied module to recursively search directories. So why not use it instead of Rake. Then your script won’t have a dependency on Rake.

Well, in short:

  1. Rake provides two functions, recursive directory searching and line by line searching (note that egrep is a FileList method provided by Rake, and is not the same as the grep method provide by Ruby arrays). Find only handles the directory search thing.
  2. I almost never use a Ruby installation without Rake installed as well. So the extra dependency is not a concern to me.
  3. Since I use Rake all the time, the syntax is at my fingertips. I don’t use Find often enough to use it without checking RI.
  4. The Find version of the script is several times longer than the Rake version (included below for comparison).

However …

The find version has one advantage over the Rake version in that it does not need to pull in the entire list of files into memory at once. That may be important to you.

Comparisons

Here’s the original version again:

  require 'rake'
  FileList["**/*.rb"].egrep(Regexp.new(ARGV.first))

And here is the Find version:

  require 'find'
  RE = Regexp.new(ARGV[0])
  Find.find('.') do |fn|
    next if fn =~ /(^(\.svn|CVS)$)/
    next unless fn =~ /\.rb$/
    open(fn) do |file|
      lines = 0
      file.each do |line|
        lines += 1
        puts "#{fn}:#{lines}:#{line}" if line =~ RE
      end
    end
  end

Update (31/Aug/06)

James Edward Grey II points out that my find example could use a little makeover. First, the regex that causes .svn directories to be skipped is incorrect, it only skips the actual directory, not the contents of the directory. Removing the ”$” from the match will fix that, but he provides a better solution. Replace that entire line with:

    Find.prune if fn =~ /(^(\.svn|CVS)$)/ 

I totally forgot about prune.

A minor correction is the use of the lines counter. He suggests either using each_with_index (I should have thought of that), or better yet, use file.lineno.

Thanks James!


comments

Using Rake as a Library   26 Aug 06
[ print link all ]

I’ve been exploring using Rake as a Ruby Library.

Find In Code

Lately, I’ve been experimenting with using Rake as a normal library in regular Ruby scripts (rather than in Rakefiles). It turns out there are some very useful things you can do.

For example, to locate a particular piece of code in a not so small Ruby project, I often use the following command line:

    $ find . -name '*.rb' | xargs grep -ni "snippet of code" 

This quickly searches all my Ruby files and displays matching lines with file names and line numbers. However, the line is long to type and won’t work in windows (unless you have cygwin installed).

So I wrote the following Ruby program and saved it in a file called “fic” (Find In Code):

  #!/usr/bin/env ruby
  require 'rake'
  FileList["**/*.rb"].egrep(Regexp.new(ARGV.first))

An Enhanced Version

This proved so useful, that I enhanced the code to allow for searching for files ending in arbitrary extendsions and to handle more matching options on the command line.

Here’s the enhanced version:

  #!/usr/bin/env ruby
  # -*- ruby -*-

  exts = ['.rb']
  if ARGV[0] =~ /^\.[a-zA-Z0-9]+$/
    exts = []
    while ARGV[0] =~ /^\.[a-zA-Z0-9]+$/
      exts << ARGV.shift
    end
  end

  ext = "{" + exts.join(',') + "}" 

  if ARGV.size < 1
    puts "Usage: #{File.basename($0)} [.ext ...] pattern ..." 
    exit 1
  end

  require 'rake'
  FileList["**/*#{ext}"].egrep(Regexp.new(ARGV.join('|')))

And if you are using Emacs …

If you are using Emacs, and you have my “visit source” code loaded, all you need to do is put the cursor on the line you are interested in and press F2.

Oh, you would like to see the elisp code for “visit source”? Let’s see, where did I put that?

  $ cd .elisp/
  $ fic .el jw-visit-source
  ini/ini-cust.el:131:(defun jw-visit-source ()
  ini/ini-zkeys.el:7:(global-set-key [f2] 'jw-visit-source)    

Now, I move the cursor to the first line and press F2, and I find:

  ;;; Source File Visiting =============================================

  (defun jw-current-line ()
    "Return the current line." 
    (let
        ((bol (save-excursion (beginning-of-line)(point)))
         (eol (save-excursion (end-of-line)(point))))
      (buffer-substring bol eol) ))

  (defun jw-extract-file-lines (line)
    "Extract a list of file/line pairs from the given line of text." 
    (let*
        ((unix_fn "[^ \t\n\r\"'([<{]+")
         (dos_fn  "[a-zA-Z]:[^\t\n\r\"'([<{]+")
         (flre (concat "\\(" unix_fn "\\|" dos_fn "\\):\\([0-9]+\\)"))
         (start nil)
         (result nil))
      (while (string-match flre line start)
        (setq start (match-end 0))
        (setq result
              (cons (list 
                     (substring line (match-beginning 1) (match-end 1))
                     (string-to-int (substring line (match-beginning 2) (match-end 2))))
                    result)))
      result))

  (defun jw-select-file-line (candidates)
    "Select a file/line candidate that references an existing file." 
    (cond ((null candidates) nil)
          ((file-readable-p (caar candidates)) (car candidates))
          (t (jw-select-file-line (cdr candidates))) ))

  (defun jw-visit-source ()
    "If the current line contains text like '../src/program.rb:34', visit 
  that file in the other window and position point on that line." 
    (interactive)
    (let* ((line (jw-current-line))
           (candidates (jw-extract-file-lines line))
           (file-line (jw-select-file-line candidates)))
      (cond (file-line
             (find-file-other-window (car file-line))
             (goto-line (cadr file-line)) )
            (t 
             (error "No source location on line.")) )))

comments

Tuning Performance   26 Aug 06
[ print link all ]

Uncle Bob has a couple examples of why performance tuning almost always has surprises in store for you.

Not What You Expect!

Bob Martin (Uncle Bob) relates a couple of stories on performance tuning and why your expectations are nearly always wrong.

The moral of the story … measure, measure, measure whenever you have a performance problem. The root cause is prabably not where you expect it to be.


comments

Announcing the RailsEdge Conference   08 Aug 06
[ print link all ]

Rails Edge

This is exciting. I have been wanting to talk about this for some time. Well, the time has finally come and I’m happy to announce that the Pragmatic Programmers are putting together a series of regional conferences called The Rails Edge.

The conference will be a single track, so all the speakers at the conference will be available for interaction at all the talks, not just during their own presentation. The speaker selection is great (I feel humbled to be a member of the group). We are hoping that it will a dynamic experiance with the speakers interacting, not only with the audience, but with the other members of the staff.

So, if you didn’t make it into the “Lucky 250” club going to RubyConf this year, here is another chance to meet and greet Rubyists in your area.

I hope to see you there.

Update: You can read the official Pragmatic Programmer’s Announcment too.


comments

Updates to Ruse Wiki Software   08 Jul 06
[ print link all ]

The Ruse wiki software gets some updates.

Some Changes to Ruse

I’ve been really pleased with the way Ruse has been handling wiki related spam on the Ruby Garden site. Now that Ruse has been deployed for a while, I’ve tweaked a few things to make it even easier to clean up spam.

  • Queued Review Mode: The original RubyGarden wiki took an annoying number of clicks to clean a page of spam. Ruse has the cleanup down to one click, but it takes an additional two clicks to get to the next page to review. (goto to review page, choose next page to review). The new Queued Review mode will now automatically advance to the next page for reviewing, bringing you to a true “one click per page” review mode. Click on the “Work from Queue” link on the review page.
  • Bug Fix: The review page now correctly displays the differences for the version you are reviewing. The original behavior incorrectly showed the wrong revisions in the differences section (although the page content was correct).
  • Minor changes and fixes: The size of the log page is now configurable (rather than the fixed 25 entry size). Demotion during review is now properly logged.

Future Directions

One technique that has proved very useful in preventing spam is the content filtering that Ruse is able to do. We download the “bad url” list from chonqged.org nightly and will reject all posts URL that match that list. Currently, we can add additional URLS to the blacklist by editting a configuration file on the server. Future versions of Ruse might add a web interface to the local blacklist.


comments

Fireworks on the Fourth of July   08 Jul 06
[ print link all ]

We had some fireworks on the 4th of July … Unfortunately they weren’t the usual kind.

Fireworks on the 4th

It is a tradition in the US to fire off fireworks on the 4th of July in celebration of Independence day. Well, the Weirich household had some fireworks, but it’s not what you would expect.

What we thought at the time was a really loud firecracker from the neighbors turned out to be a close lightening strike, probably hitting the phone line. I discovered later that my DSL router (attached to the phone line) was dead (as in no lights, no power).

After a picking up a new DSL router on Wednesday from the phone company, it still didn’t connect and the phone company promised to send a technician out one Friday. Arrgh! Three days with no internect! Sigh.

The technician arrives Friday, checks out the wiring and decides to upgrade the “system”. Part of the upgrade is another new DSL router (different model from the replacement unit I picked up Wednesday). We connect up the laptop to the DSL router and everything looks great.

So once the phone guy leaves, I start hooking up the rest of the network: firewall router, network hub, wireless base station, and the other computers in the house.

And nothing works.

It looks like the lightening not only took out my DSL router, but it also got the firewall, network hub and wireless base station. Fortunately, I have another firewall and hub in my spare parts box.

Bad Cables?

I’m not certain of the total extent of the damage yet. The cables running to the other computers in the basement seem to be bad now (tested by against my working laptop). Since I don’t have long enough replacement cables, I haven’t checked out the network cards in two of the computers yet.

I am a bit surprised by the cables going bad. I understand delicate electronic equipment fried by lightening, but the cables are just wires and connectors. For them to be bad must mean the strike was strong enough to short them out somehow. And that doesn’t sound good for the network cards at the other end of the cable. Sigh.

On the good side, the my desktop computer that sits right next to the network equipment is up and running on the network. So one would that the network card in it is ok. However, it does seem to be a bit slow when browsing web pages. I mean really slow. I timed it against my laptop. What loads two seconds on my laptop takes over 20 seconds on the desktop. But that sounds more like a network configuration error than a hardware issue. Sigh, more work to do.

At any rate, it looks like there will be a run to the local computer store soon.


comments

Delete Your RubyGems Cache   02 Jul 06
[ print link all ]

Several people have been having RubyGems issues.

Deleting Your RubyGems Cache

Several people have been reporting problems where RubyGems doesn’t find a gem on RubyForge, or gives other strange errors. It seems there was a gem on RubyForge that gave the Gem indexing software some headaches. And as a result, the gem index was corrupted. If you downloaded the corrupt index, then you may be experiencing strange problems as well.

The good news is that the problem is easy ot fix. Just delete the gem index file. You can find it in the directory reported by the “gem env gemdir” command. For example:

$ gem env gemdir
PATH_TO_DEFAULT_GEM_REPOSITORY
$ rm PATH_TO_DEFAULT_GEM_REPOSITORY/souce_cache

Where PATH_TO_DEFAULT_GEM_REPOSITORY is your default gem repository.

If you run on a Unix system, you default gem repository is probably non-writable from your regular user account. In that case, you will need to use “sudo” (or its equivalent) on the “rm” command. You will probably also have a secondary writable cache in your .gem directory that you will want to delete.

$ rm $HOME/.gem/source_cache

The next time you run gems, will will refresh the cache by downloading the index from scratch.


comments

 

Formatted: 21-May-12 10:58
Feedback: jim@weirichhouse.org