A series of terrible ideas for Ruby
Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
spec
.gitignore
.rspec
Gemfile
README.md
Rakefile
terrible_things.gemspec

README.md

Terrible Things

Inspired by a question posed by oddmunds, I decided to put together a gem of terrible ideas. Here are some of its current features.

Current Features

Method Auto Correction

Ever get tired of NameError's being raised as a result of a silly typo in a method name? Your hours of weeping and gnashing of teeth can now be resolved effortlessly. This idea was put forth by oddmunds on freenode.

Toggle auto correction on your objects:

class MyClass
  def initialize
    enable_auto_correct
  end

  def my_super_method
    # Make sweet music
  end

  def my_other_method
    my_spure_method
  end
end

my_inst = MyClass.new
my_inst.my_other_method

With auto-correction enabled, you can rest assured that my_ins.my_other_method will indeed make sweet music.

Have a typo buried in code but don't have time to track it down? No problem! You can either manually toggle auto-correction:

def from_str_to_sym str
  str.to_sym
end

def from_sym_to_str sym
  sym.to_s
end

def my_convoluted_method *args
  enable_auto_correct
  args.map do |a|
    if a.to_i > 0
      a.to_i * 5
    else
      case a
      when String
        from_str_to_smy a
      when Symbol
        from_sym_tos_tr a
      else
      end
    end
    disable_auto_correct
  end
end

or, you can evaluate a specific block with auto-correction enabled:

def my_convoluted_method *args
  args.map do |a|
    if a.to_i > 0
      a.to_i * 5
    else
      with_auto_correction do
        case a
        when String
          from_str_to_smy a
        when Symbol
          from_sym_tos_tr a
        else
        end
      end
    end
  end
end

If you need further proof of auto-correction's capabilities, the method disable_auto_correct is deliberately misspelled as disable_auto_corretc. This gives auto-correction one last chance to prove its love to you before shutting itself off.

Write Code Confidently!

How can you write "confident code" when you're not quite sure what wicked sorcery is being performed behind opaque method names? Fear not, we're working on a solution that will let you code confidently with a clear conscience: meet are_you_sure?.

class MyModel < ActiveRecord::Base

  def self.recent min_created_at = nil   
    # Who knows what dark arts are being performed by arel's
    # "convenient" methods?
    min_created_at ||= Time.now.advance(:days => -5)
    are_you_sure? do
      where "created_at >= ?", min_created_at
    end
  end

end

By forcing users to confirm method invocations, you are freed from all expectations and liabilities. After all, if the end users confirm the method call, any blame for unintended results falls squarely on their shoulders. By examining the shortcomings of EULA prompts, we opted to make "deny" the default behavior to prevent users from blindly pressing the return key!

While this method is certainly handy, we intend to make it even handier in the future. Right now we are only trapping method invocations, but with a little luck, we will be able to capture more than that soon!

Monadic NilClass

The Maybe monad is powerful stuff, and while others have proposed custom classes to encapsulate this behavior, doing so loses Ruby's notion of nil being falsey, so we bake this goodness right into NilClass!

def method_that_might_return_nil
  # ...
end

def method_using_monadic_nils
  with_maybe do
    method_that_might_return_nil.keen_instance_method.reverse
  end
end

Wrapped Instantiation

If you believe that method decorating is powerful stuff, then I think you'll agree: you'd be hard pressed to find more power than what you get from decorating Object::new. With TerribleThings, it's dead simple:

TCPSocket.push_new do |chain, klass|
  $stdout.puts "A new TCP/IP Socket is being created!"
  chain.call
end

At some point or another, you will want to call the chain proc, if you eventually want a TCPSocket to be instantiated. However, if you don't do it right away, you can get....

Pretty Lazy

Do you wish only the objects that you actually need would be instantiated? Don't you wish map only transformed those elements that actually get used later? Well, wish no more:

enable_lazy_evaluation
arr = Array.new()

puts "Creating array"
puts "-"*15
10.times do |i|
  arr << i
end

puts "Mapping array"
puts "-"*15
mapped = arr.map do |i|
  puts "Working with #{i}"
  i * 2
end

puts "Mapping again"
puts "-"*15
mapped2 = mapped.map do |i|
  puts "Reworking with #{i}"
  i + 3
end.take(3)

puts "Showing results"
puts "-"*15
puts mapped2.inspect

When run, this example will produce output similar to the following:

Creating array
---------------
Mapping array
---------------
Mapping again
---------------
Working with 0
Reworking with 0
Working with 1
Reworking with 2
Working with 2
Reworking with 4
Showing results
---------------
[3, 5, 7]

Looking for an exaple that's a little more concise? How about this fella:

enable_lazy_evaluation
r = Range.new 0, (1/0.0)
puts r.map { |i| 2 * i + 7 }.take(6)

which prints:

7
9
11
13
15
17

Unfortunately, we can't ensure that all object instantiations are lazily evaluated as [ 1, 2, 3] and { a: 1, b: 2, c: 3} are baked in to Ruby's grammar and don't rely on Array.new and Hash.new, respectively; however, we can get pretty close!

How do we know when it's time to evaluate the lazy promise, you ask? It's very simple! The only clear indication that you really need a value is when you call #inspect or #to_s on said value!

Future Features

  • Let's take probabilistic models to their natural conclusion: guessing which arguments a user meant to pass to a method, or determining the appropriate type a method should return.

  • Do some "on-the-fly" benchmarking to identify what methods are taking too long (or are too complex) and stop calling them for great scalability.

I should have more to put here by this weekend. Maybe not, it's hard to say how long I want to keep this going.

Contributing

Really?

Really?

I suppose if you want to, fork and send a pull request. If what you've got shows promise, I'll probably merge it in.