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 +66 -47
- data/lib/must/rule.rb +31 -13
- data/lib/must/same_struct.rb +47 -0
- data/lib/must/version.rb +1 -1
- data/test/must_test.rb +3 -0
- data/test/struct_test.rb +57 -0
- metadata +6 -4
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
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
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
|
|
data/test/struct_test.rb
ADDED
@@ -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:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
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-
|
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: []
|