Project

must

0.01
No commit activity in last 3 years
No release in over 3 years
add Object#must method to constrain its origin and conversions
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

>= 0
 Project Readme
Must
====
  add Object#must method to constrain its origin and conversions

  # can write like this
    num = params[:num].to_i.must.match(1..300) {10}

  # rather than
    num = params[:num].to_i
    num = 10 unless (1..300) === num

  and has duck-type features

    1.must.duck?(:to_s)  # => true
    io.must.duck(:write) { io.extend Writable }

  and has struct assetions

    pages = [{:name=>"...", :url=>"...",} ...]
    pages.must.struct([Hash])

  You want Boolean class? try this!

    flag = hash["flag"].must(true, false)


Asking Methods
==============
  be      : check whether object equals to the argument
  kind_of : check whether object is a kind of the arguments
  coerced : check whether object can be coerced to the argument
  blank   : check whether object is blank?
  exist   : check whether object is not nil (NOTE: false is ok)


Logical Methods
===============
  not : logical NOT


Nop Methods
===========
  a  : return self
  an : return self

These effect nothing but exist only for English grammar.


Duck Methods
============
  duck("foo")  : check whether object responds to "foo" method.
  duck(:foo)   : same above
  duck(".foo") : same above
  duck("#foo") : check whether object has "foo" instance method. (tested only in class/module)
  duck?(...)   : acts same as "duck", but this returns a just boolean
  duck!("foo") : if foo exists, call it. otherwise raises Invalid


Struct Methods
============
  struct(...)  : check whether object has a same struct with ...
  struct?(...) : acts same as "struct", but this returns a just boolean


Basic Examples
==============

# test its value exactly
  1.must.be 1              # => 1
  [1,2,3].must.be [1,2,3]  # => [1,2,3]

# exceptions
  1.must.be []             # Must::Invalid exception
  1.must.be([]) {:ng}      # => :ng
  1.must.be(1) {:ng}       # => 1

# as default value
  name = params[:name].must.not.be.blank{ "No name" }

# existing test
  1.must.exist             # => 1
  nil.must.exist           # Must::Invalid exception
  false.must.exist         # => false

# test class : ensures that a class of the object is one of given arguments
  1.must.be.kind_of(Integer)         # => 1
  1.must.be.kind_of(Integer, Array)  # => 1
  [].must.be.kind_of(Integer, Array) # => []
  1.must.be.kind_of(String, Array)   # Must::Invalid: expected String/Array but got Fixnum

# must(*args) is a syntax sugar for kind_of
  1.must(Integer)          # same as "1.must.be.kind_of(Integer)"

# coercing : looks like kind_of except converting its value if possible
  1.must.be.coerced(Integer, String => proc{|val| val.to_i})    # => 1
  "1".must.be.coerced(Integer, String => proc{|val| val.to_i})  # => 1
  "1".must.be.coerced(Integer, String => :to_i)                 # => 1     (NOTE: inline Symbol means sending the method)
  "1".must.be.coerced(Integer, Symbol, String => proc{:to_i})   # => :to_i (NOTE: use proc to return Symbol itself)

# struct assertions

  uris = build_uris		# ex) [{:host=>"...", :port=>"..."}, ...]
  uris.must.struct([Hash])


Actual Examples
===============

1)

  normal code:

    def set_reader(reader)
      if reader.is_a?(CSV::Reader)
        @reader = reader
      elsif file.is_a?(String)
        @reader = CSV::Reader.create(i)
      elsif file.is_a?(Pathname)
        @reader = CSV::Reader.create(reader.read)
      else
        raise 'invalid reader'
      end
    end

  refactor above code with must plugin
 
    def set_reader(reader)
      @reader = reader.must.be.coerced(CSV::Reader, Pathname=>:read, String=>{|i| CSV::Reader.create(i)}) {raise 'invalid reader'}
    end

2)

  class DateFolder
    def initialize(date)
      @date = date.must.be.coerced(Date, String=>proc{|i| Date.new(*i.scan(/\d+/).map{|i|i.to_i})})
    end
  end

  # this can accept both formats

  DateFolder.new Date.today
  DateFolder.new "2008-12-9"


NOTE
====
"must(*args)" is a shortcut for not "be(*args)" but "kind_of(*args)" and "struct(*args)".

  1.must(1)        # => 1
  1.must(Fixnum)   # => 1
  1.must(2)        # => 1    # NOTE
  1.must.be(2)     # Invalid
  Fixnum.must(1)   # => Fixnum


Bundled Class
=============

  struct = Must::StructInfo.new({"1.1" => {"jp"=>[{:a=>0},{:b=>2}]}})
  struct.types   # => [Hash, String, Array, Symbol, Fixnum]
  struct.compact # => {String=>{String=>[{Symbol=>Fixnum}]}}
  struct.inspect # => "{String=>{String=>[{Symbol=>Fixnum}]}}"


TODO
====
  * add proper error messages


Install
=======
  gem install must

  % irb -r rubygems -r must
  irb(main):001:0> 1.must.be 1
  => 1


Github
======
  http://github.com/maiha/must


Author
======
  maiha@wota.jp