-
Notifications
You must be signed in to change notification settings - Fork 0
maiha/must
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
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