must 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,7 +1,6 @@
1
1
  Must
2
2
  ====
3
-
4
- add Object#must method to constrain its origin and conversions
3
+ add Object#must method to constrain its origin and conversions
5
4
 
6
5
  # can write like this
7
6
  num = params[:num].to_i.must.match(1..300) {10}
@@ -10,80 +9,93 @@ add Object#must method to constrain its origin and conversions
10
9
  num = params[:num].to_i
11
10
  num = 10 unless (1..300) === num
12
11
 
13
- and has duck features
12
+ and has duck-type features
14
13
 
15
14
  1.must.duck?(:to_s) # => true
16
15
  io.must.duck(:write) { io.extend Writable }
17
16
 
17
+ and has struct assetions
18
+
19
+ pages = [{:name=>"...", :url=>"...",} ...]
20
+ pages.must.struct([Hash])
21
+
18
22
 
19
23
  Asking Methods
20
24
  ==============
21
-
22
- be : check whether object equals to the argument
23
- kind_of : check whether object is a kind of the arguments
24
- coerced : check whether object can be coerced to the argument
25
- blank : check whether object is blank?
26
- exist : check whether object is not nil (NOTE: false is ok)
25
+ be : check whether object equals to the argument
26
+ kind_of : check whether object is a kind of the arguments
27
+ coerced : check whether object can be coerced to the argument
28
+ blank : check whether object is blank?
29
+ exist : check whether object is not nil (NOTE: false is ok)
27
30
 
28
31
 
29
32
  Logical Methods
30
33
  ===============
31
-
32
- not : logical NOT
34
+ not : logical NOT
33
35
 
34
36
 
35
37
  Nop Methods
36
38
  ===========
37
-
38
- a : return self
39
- an : return self
39
+ a : return self
40
+ an : return self
40
41
 
41
42
  These effect nothing but exist only for English grammar.
42
43
 
43
44
 
44
45
  Duck Methods
45
46
  ============
46
- duck("foo") : check whether object responds to "foo" method.
47
- duck(:foo) : same above
48
- duck(".foo") : same above
49
- duck("#foo") : check whether object has "foo" instance method. (tested only in class/module)
50
- duck?(...) : acts same as "duck", but this returns a just boolean
47
+ duck("foo") : check whether object responds to "foo" method.
48
+ duck(:foo) : same above
49
+ duck(".foo") : same above
50
+ duck("#foo") : check whether object has "foo" instance method. (tested only in class/module)
51
+ duck?(...) : acts same as "duck", but this returns a just boolean
52
+
53
+
54
+ Struct Methods
55
+ ============
56
+ struct(...) : check whether object has a same struct with ...
57
+ struct?(...) : acts same as "struct", but this returns a just boolean
51
58
 
52
59
 
53
60
  Basic Examples
54
61
  ==============
55
62
 
56
63
  # test its value exactly
57
- 1.must.be 1 # => 1
58
- [1,2,3].must.be [1,2,3] # => [1,2,3]
64
+ 1.must.be 1 # => 1
65
+ [1,2,3].must.be [1,2,3] # => [1,2,3]
59
66
 
60
67
  # exceptions
61
- 1.must.be [] # Must::Invalid exception
62
- 1.must.be([]) {:ng} # => :ng
63
- 1.must.be(1) {:ng} # => 1
68
+ 1.must.be [] # Must::Invalid exception
69
+ 1.must.be([]) {:ng} # => :ng
70
+ 1.must.be(1) {:ng} # => 1
64
71
 
65
72
  # as default value
66
- name = params[:name].must.not.be.blank{ "No name" }
73
+ name = params[:name].must.not.be.blank{ "No name" }
67
74
 
68
75
  # existing test
69
- 1.must.exist # => 1
70
- nil.must.exist # Must::Invalid exception
71
- false.must.exist # => false
76
+ 1.must.exist # => 1
77
+ nil.must.exist # Must::Invalid exception
78
+ false.must.exist # => false
72
79
 
73
80
  # test class : ensures that a class of the object is one of given arguments
74
- 1.must.be.kind_of(Integer) # => 1
75
- 1.must.be.kind_of(Integer, Array) # => 1
76
- [].must.be.kind_of(Integer, Array) # => []
77
- 1.must.be.kind_of(String, Array) # Must::Invalid: expected String/Array but got Fixnum
81
+ 1.must.be.kind_of(Integer) # => 1
82
+ 1.must.be.kind_of(Integer, Array) # => 1
83
+ [].must.be.kind_of(Integer, Array) # => []
84
+ 1.must.be.kind_of(String, Array) # Must::Invalid: expected String/Array but got Fixnum
78
85
 
79
86
  # must(*args) is a syntax sugar for kind_of
80
- 1.must(Integer) # same as "1.must.be.kind_of(Integer)"
87
+ 1.must(Integer) # same as "1.must.be.kind_of(Integer)"
81
88
 
82
89
  # coercing : looks like kind_of except converting its value if possible
83
- 1.must.be.coerced(Integer, String => proc{|val| val.to_i}) # => 1
84
- "1".must.be.coerced(Integer, String => proc{|val| val.to_i}) # => 1
85
- "1".must.be.coerced(Integer, String => :to_i) # => 1 (NOTE: inline Symbol means sending the method)
86
- "1".must.be.coerced(Integer, Symbol, String => proc{:to_i}) # => :to_i (NOTE: use proc to return Symbol itself)
90
+ 1.must.be.coerced(Integer, String => proc{|val| val.to_i}) # => 1
91
+ "1".must.be.coerced(Integer, String => proc{|val| val.to_i}) # => 1
92
+ "1".must.be.coerced(Integer, String => :to_i) # => 1 (NOTE: inline Symbol means sending the method)
93
+ "1".must.be.coerced(Integer, Symbol, String => proc{:to_i}) # => :to_i (NOTE: use proc to return Symbol itself)
94
+
95
+ # struct assertions
96
+
97
+ uris = build_uris # ex) [{:host=>"...", :port=>"..."}, ...]
98
+ uris.must.struct([Hash])
87
99
 
88
100
 
89
101
  Actual Examples
@@ -125,24 +137,31 @@ Actual Examples
125
137
  DateFolder.new "2008-12-9"
126
138
 
127
139
 
140
+ NOTE
141
+ ====
142
+ "must(*args)" is a shortcut for not "be(*args)" but "kind_of(*args)" and "struct(*args)".
143
+
144
+ 1.must(1) # => 1
145
+ 1.must(Fixnum) # => 1
146
+ 1.must(2) # => 1 # NOTE
147
+ 1.must.be(2) # Invalid
148
+ Fixnum.must(1) # => Fixnum
149
+
150
+
128
151
  Install
129
152
  =======
153
+ gem install must
130
154
 
131
- gem install must
132
-
133
- % irb -r rubygems -r must
134
- irb(main):001:0> 1.must.be 1
135
- => 1
155
+ % irb -r rubygems -r must
156
+ irb(main):001:0> 1.must.be 1
157
+ => 1
136
158
 
137
159
 
138
160
  Github
139
161
  ======
140
-
141
- http://github.com/maiha/must
162
+ http://github.com/maiha/must
142
163
 
143
164
 
144
165
  Author
145
166
  ======
146
-
147
- maiha@wota.jp
148
-
167
+ maiha@wota.jp
data/lib/must/rule.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require "must/same_struct"
2
3
 
3
4
  module Must
4
5
  class Rule
@@ -39,8 +40,9 @@ module Must
39
40
  end
40
41
 
41
42
  def kind_of(*targets)
42
- valid?(targets.any?{|klass| object.is_a? klass}) {
43
- target = targets.map{|i| i.name}.join('/')
43
+ bool = targets.any?{|klass| is_a?(klass)}
44
+ valid?(bool) {
45
+ target = targets.map{|i| instance?(i) ? i.class.name : i.name}.join('/')
44
46
  raise Invalid, "expected #{target} but got #{object.class}"
45
47
  }
46
48
  end
@@ -58,14 +60,6 @@ module Must
58
60
  end
59
61
  end
60
62
 
61
- def block_or_throw(&block)
62
- if block
63
- block.call
64
- else
65
- raise Invalid
66
- end
67
- end
68
-
69
63
  def coerced(*types, &block)
70
64
  coecings ||= types.last.is_a?(Hash) ? types.pop : {}
71
65
  already_coerced ||= Set.new
@@ -94,13 +88,21 @@ module Must
94
88
  end
95
89
  end
96
90
 
91
+ def struct?(target)
92
+ Must::SameStruct.check(@object, target)
93
+ end
94
+
95
+ def struct(target, &block)
96
+ valid?(struct?(target), &block)
97
+ end
98
+
97
99
  private
98
- def instance?
99
- @object.class.to_s !~ /\A(Class|Module)\Z/o
100
+ def instance?(obj)
101
+ obj.class.to_s !~ /\A(Class|Module)\Z/o
100
102
  end
101
103
 
102
104
  def instance_method_defined?(method_name)
103
- return false if instance?
105
+ return false if instance?(@object)
104
106
  !! @object.instance_methods.find { |m| m.to_s == method_name.to_s}
105
107
  end
106
108
 
@@ -108,5 +110,21 @@ module Must
108
110
  @object.respond_to?(method_name)
109
111
  end
110
112
 
113
+ def is_a?(klass)
114
+ if instance?(klass)
115
+ struct?(klass)
116
+ else
117
+ @object.is_a? klass
118
+ end
119
+ end
120
+
121
+ def block_or_throw(&block)
122
+ if block
123
+ block.call
124
+ else
125
+ raise Invalid
126
+ end
127
+ end
128
+
111
129
  end
112
130
  end
@@ -0,0 +1,47 @@
1
+ module Must
2
+ module SameStruct
3
+ def self.check(src, dst)
4
+ case src
5
+ when Hash
6
+ return true if dst == Hash
7
+ dst.must(Hash)
8
+ return true if src.empty?
9
+ return true if dst.empty?
10
+ key1, val1 = src.first
11
+ key2, val2 = dst.first
12
+ key1.must(key2)
13
+ val1.must(val2)
14
+
15
+ when Array
16
+ return true if dst == Array
17
+ dst.must(Array)
18
+ return true if src.empty?
19
+ return true if dst.empty?
20
+ src.first.must(dst.first)
21
+
22
+ else
23
+ # 1.must.struct(2)
24
+ # 1.must.struct(Integer)
25
+ dst_class = classify(dst)
26
+ return true if classify(src).ancestors.include?(dst_class)
27
+
28
+ # Fixnum.must.struct(2)
29
+ return true if class?(src) and src.ancestors.include?(dst_class)
30
+
31
+ return false
32
+ end
33
+ return true
34
+
35
+ rescue Must::Invalid
36
+ return false
37
+ end
38
+
39
+ def self.classify(obj)
40
+ class?(obj) ? obj : obj.class
41
+ end
42
+
43
+ def self.class?(obj)
44
+ obj.class.to_s =~ /\A(Class|Module)\Z/o
45
+ end
46
+ end
47
+ end
data/lib/must/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Must
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
data/test/must_test.rb CHANGED
@@ -32,6 +32,9 @@ class MustTest < Test::Unit::TestCase
32
32
  assert_equal "ok", "ok".must(String)
33
33
  assert_raises(Invalid) {"ok".must(Integer)}
34
34
  assert_equal "ok", "ok".must(Integer, String)
35
+
36
+ # NOTE: this passes because 1(integer) is a kind of 2(integer)
37
+ assert_equal 1, 1.must(2)
35
38
  end
36
39
  end
37
40
 
@@ -0,0 +1,57 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require File.join(File.dirname(__FILE__), '../init')
4
+
5
+ class StructTest < Test::Unit::TestCase
6
+ def test_struct_test_successed
7
+ assert_equal true, [].must.struct?([])
8
+ assert_equal true, 1.must.struct?(Integer)
9
+ assert_equal true, 1.must.struct?(Fixnum)
10
+ assert_equal true, 1.must.struct?(2)
11
+ assert_equal true, Fixnum.must.struct?(Fixnum)
12
+ assert_equal true, Fixnum.must.struct?(2)
13
+
14
+ obj = {"foo" => 1}
15
+ assert_equal true, obj.must.struct?({String => Integer})
16
+
17
+ obj = {"foo" => [{:a=>1}, {:a=>3}]}
18
+ assert_equal true, obj.must.struct?(Hash)
19
+ assert_equal true, obj.must.struct?({String => Array})
20
+ assert_equal true, obj.must.struct?({String => [Hash]})
21
+ end
22
+
23
+ def test_struct_test_failed
24
+ assert_equal false, [].must.struct?({})
25
+ assert_equal false, Integer.must.struct?(Fixnum)
26
+
27
+ obj = {"foo" => 1}
28
+ assert_equal false, obj.must.struct?(String)
29
+ assert_equal false, obj.must.struct?(Array)
30
+ assert_equal false, obj.must.struct?([String])
31
+ assert_equal false, obj.must.struct?({String => String})
32
+
33
+ obj = [{:a=>1}, {:a=>3}]
34
+ assert_equal false, obj.must.struct?([Array])
35
+
36
+ obj = {"foo" => [{:a=>1}, {:a=>3}]}
37
+ assert_equal false, obj.must.struct?({String => Hash})
38
+ assert_equal false, obj.must.struct?({String => [Array]})
39
+ end
40
+
41
+ def test_struct_complicated
42
+ def ok(obj)
43
+ assert_equal obj, obj.must.struct({String => [Hash]})
44
+ end
45
+ def ng(obj)
46
+ assert_raises(Invalid){ obj.must.struct({String => [Hash]})}
47
+ end
48
+
49
+ ok({})
50
+ ng []
51
+ ok "foo" => []
52
+ ng "foo" => {"gp"=>"-1"}
53
+ ok "foo" => [{"gp"=>"-1"}]
54
+ ok "foo" => [{"sid"=>"45064"}, {"gp"=>"-1"}]
55
+ ng "foo" => [[{"sid"=>"45064"}]]
56
+ end
57
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: must
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 3
10
- version: 0.2.3
9
+ - 4
10
+ version: 0.2.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - maiha
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-26 00:00:00 +09:00
18
+ date: 2012-01-27 00:00:00 +09:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -35,12 +35,14 @@ files:
35
35
  - install.rb
36
36
  - lib/must.rb
37
37
  - lib/must/rule.rb
38
+ - lib/must/same_struct.rb
38
39
  - lib/must/version.rb
39
40
  - must.gemspec
40
41
  - test/coerced_test.rb
41
42
  - test/duck_test.rb
42
43
  - test/match_test.rb
43
44
  - test/must_test.rb
45
+ - test/struct_test.rb
44
46
  has_rdoc: true
45
47
  homepage: http://github.com/maiha/must
46
48
  licenses: []