0.0
No commit activity in last 3 years
No release in over 3 years
Flexibility is a mix-in for ruby classes that allows you to easily define methods that can take a mixture of positional and keyword arguments.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 3
~> 0.8
 Project Readme

Flexibility is a mix-in for ruby classes that allows you to easily #define methods that can take a mixture of positional and keyword arguments.

For example, suppose we define

class Banner
  include Flexibility

  define( :show,
    message: [
      required,
      validate { |s| String === s },
      transform { |s| s.upcase }
    ],
    width:   [
      default { @width },
      validate { |n| 0 <= n }
    ],
    symbol:  default('*')
  ) do |message,width,symbol,unused_opts|
    width = [ width, message.length + 4 ].max
    puts "#{symbol * width}"
    puts "#{symbol} #{message.ljust(width - 4)} #{symbol}"
    puts "#{symbol * width}"
  end

  def initialize
    @width = 40
  end
end

Popping over to IRB, we could use Banner#show with keyword arguments,

irb> banner = Banner.new
irb> banner.show( message: "HELLO", width: 10, symbol: '*' )
**********
* HELLO  *
**********
 => nil

positional arguments

irb> banner.show( "HELLO WORLD!", 20, '#' )
####################
# HELLO WORLD!     #
####################
 => nil

or a mix

irb> banner.show( "A-HA", symbol: '-', width: 15 )
---------------
- A-HA        -
---------------
 => nil

The keyword arguments are taken from the last argument, if it is a Hash, while the preceeding positional arguments are matched up to the keyword in the same position in the argument description.

Flexibility also allows the user to run zero or more callbacks on each argument, and includes a number of callback generators to specify a #default value, mark a given argument as #required, #validate an argument, or #transform an argument into a more acceptable form.

Continuing our prior example, this means Banner#show only requires one argument, which it automatically upper-cases:

irb> banner.show( "celery?" )
****************************************
* CELERY?                              *
****************************************

And it will raise an error if the message is missing or not a String, or if the width argument is negative:

irb> banner.show
!> ArgumentError: Required argument :message not given
irb> banner.show 8675309
!> ArgumentError: Invalid value 8675309 given for argument :message
irb> banner.show "hello", -9
!> ArgumentError: Invalid value -9 given for argument :width

Just as Flexibility#define allows the method caller to determine whether to pass the method arguments positionally, with keywords, or in a mixture of the two, it also allows method authors to determine whether the method receives arguments in a Hash or positionally:

class Banner
  opts_desc = { a: [], b: [], c: [], d: [], e: [] }
  define :all_positional, opts_desc do |a,b,c,d,e,opts|
    [ a, b, c, d, e, opts ]
  end
  define :all_keyword, opts_desc do |opts|
    [ opts ]
  end
  define :mixture, opts_desc do |a,b,c,opts|
    [ a, b, c, opts ]
  end
end
irb> banner.all_positional(1,2,3,4,5)
=> [ 1, 2, 3, 4, 5, {} ]
irb> banner.all_positional(a:1, b:2, c:3, d:4, e:5, f:6)
=> [ 1, 2, 3, 4, 5, {f:6} ]
irb> banner.all_keyword(1,2,3,4,5)
=> [ { a:1, b:2, c:3, d:4, e:5 } ]
irb> banner.all_keyword(a:1, b:2, c:3, d:4, e:5, f:6)
=> [ { a:1, b:2, c:3, d:4, e:5, f:6 } ]
irb> banner.mixture(1,2,3,4,5)
=> [ 1, 2, 3, { d:4, e:5 } ]
irb> banner.mixture(a:1, b:2, c:3, d:4, e:5, f:6)
=> [ 1, 2, 3, { d:4, e:5, f:6 } ]