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 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: []