BlockBuilder

Make callbacks easily and build blocks with ease! BlockBuilder is intended to be used with more heavy weight method calls that might require intermediate callbacks. It's almost like creating classes on the fly!

Usage

Some code samples to get you started:

File Download Example

Downloading a file probably doesn't merit the block builder, but it demonstrates the concept of callbacks.

def download_file(url, &block)

  # Configure your block here, along with defaults
  builder = BlockBuilder.build block do

    # Set optional default variables!
    set :verbose, true

    on_fail do |http_code, body|
      puts "Oh no! HTTP download failed! Code: #{http_code}"
      puts "Downloaded body: #{body}" if verbose
    end

    on_success do |body|
      puts "Downloaded file Successfully!"
    end
  end

  # Now execute your blocks! Note you don't have to explicitly
  # define any of your callbacks.
  result = download_some_file_from_internet(url)
  if result.http_code == 200
    builder.on_success.call result.body
  else
    builder.on_fail.call result.http_code, result.body
  end

end


# Now you can call the method above and change the defaults!
download_file "example.com/exists.txt" do
  on_success do |body|
    File.open('some/file.txt', 'w') {|f| f.write body }
  end
end

# Only outputs "Oh no! HTTP download failed! Code: 404"
download_file "example.com/doesnotexist.txt" do
  set :verbose, false
end

Callbacks and Remote Data

Sometimes you might connect to another service or API that contains critical information. And perhaps you need to query multiple times with this API to get the data you need. Callbacks can help with this!

 As a convention, you should always document a block builder method and let consumers of
# this method know how to use it!
# @example
#   # Here is a code sample on how to use this method, etc
def create_remote_user(username, password, &block)
  builder = BlockBuilder.build block do
    set :logger, Logger.new

    # You can force the creation of a callback using the abstract operator. By default,
    # without the abstract modifier, calling an undefined callback will simply do nothing
    abstract :on_user_created
  end

  # If any callbacks label as 'abstract' aren't defined, then code execution will
  # never reach this line.

  # Here, you may first want to create the user and create a callback. This callback
  # can then be used to quickly save an entry in the database before other operations
  # are executed. This is useful, for example, if the server crashes during the middle
  # of the request or there is a bug later in the method call. This way, at least you
  # have the user information saved in your local database and you can retrieve the
  # user information later.
  result = api.create_user username, password
  builder.on_user_created.call(result)

  # Now we can keep on executing methods and have somewhere else take care of it. Using
  # the set logger variable, we can allow someone to externally change the logger used.
  #
  # Now what if the connection timed out here? Or the server crashed? That's the point
  # of the earlier callback! It allows you to save partial data without interfering
  # with the internal logic of this method!
  start = Time.now
  result2 = api.some_very_time_intensive_call_involving(username)
  builder.logger.info "Executed time intensive call in #{Time.now - start} sec!"
  builder.on_finish_lengthy_task.call(result2)

  # Another fun use for this: collecting statistics! It might be useful to collect a
  # bunch of statistics for a particular task. However, not every consumer of your
  # method will care for statistics. Just for fun, let's only calculate some
  # statistics about this method call only if they care to calculate stats!
  #
  # We use 'builder.defined?' here because doing
  #
  #   builder.generate_statistics.call(some_lengthy_statistic_compilation)
  #
  # will compile statistics, regardless of whether 'generate_statistics' is defined.
  if builder.defined? :generate_statistics
    stats = some_lengthy_statistic_compilation
    builder.generate_statistics.call(stats)
  end


  # Now you can add any more callbacks as you see fit! And don't forget, you can still
  # return some data from this method call if you want!
end

Now you can use this method in multiple places. Here, assume this is where the code is primarily used:

create_remote_user 'user', 'password' do

  # This easily lets us use our Rails logger
  set :logger, Rails.logger

  # Remember, our abstract callback was declared, so we must define it here. In this
  # case, assume we have a Ruby on Rails model that deals with saving saved data
  on_user_created do |result|
    u = RemoteUser.new(result)
    u.some_misc_operations
    u.save!
  end
end

Finally, assume this is some benchmark suite, or something else:

# Assume this is some benchmark suite or testing file
create_remote_user 'user', 'password' do
  on_user_created do |result|
    # .. Implement here
  end

  # In this case, we might enjoy statistics
  generate_statistics do |stats|
    puts "Statistics for this operation:"
    puts " * Total execution time: #{stats.exec_time}"
    puts "..."
  end

end

Installation

Add this line to your application's Gemfile:

gem 'blockbuilder'

And then execute:

$ bundle

Or install it yourself as:

$ gem install blockbuilder