must 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|