Module: MustBe

Included in:
Object
Defined in:
lib/must_be/core.rb,
lib/must_be/basic.rb,
lib/must_be/proxy.rb,
lib/must_be/containers.rb,
lib/must_be/nonstandard_control_flow.rb

Defined Under Namespace

Modules: MustOnlyEverContain Classes: ContainerNote, Note, PairNote, Proxy

Constant Summary collapse

SHORT_INSPECT_CUTOFF_LENGTH =

Short Inspect ###

200
SHORT_INSPECT_WORD_BREAK_LENGTH =
20
SHORT_INSPECT_ELLIPSES =
"..."
NOTIFIERS =

Notifiers ###

{}
@@disabled_method_for_method =

Enable ###

Hash.new(:must_just_return)
@@disabled_handlers =
[]

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.notifierObject

should respond_to? :call with Note argument.



100
101
102
# File 'lib/must_be/core.rb', line 100

def notifier
  @notifier
end

Class Method Details

.check_pair_against_hash_cases(key, value, cases, negate = false) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/must_be/containers.rb', line 65

def self.check_pair_against_hash_cases(key, value, cases, negate = false)
  if negate
    if cases.empty?
      !key and !value
    else
      cases.all? do |c|
        c.all? do |k, v|
          not (match_any_case?(key, k) and match_any_case?(value, v))
        end
      end
    end
  else
    if cases.empty?
      key and value
    else
      cases.any? do |c|
        c.any? do |k, v|
          match_any_case?(key, k) and match_any_case?(value, v)
        end
      end
    end
  end
end

.def_notifier(constant_name, key = nil, &notifier) ⇒ Object



102
103
104
105
# File 'lib/must_be/core.rb', line 102

def def_notifier(constant_name, key = nil, &notifier)
  const_set(constant_name, notifier)
  NOTIFIERS[key] = constant_name if key
end

.disableObject



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/must_be/core.rb', line 38

def disable
  return unless enabled?
  
  @disabled_methods = instance_methods.map do |method_name|
    method_name = method_name.to_sym
    method = instance_method(method_name)
    disabled_method_name = @@disabled_method_for_method[method_name]
    alias_method method_name, disabled_method_name
    [method_name, method]
  end
  invoke_disabled_handlers
end

.enableObject



51
52
53
54
55
56
57
58
59
# File 'lib/must_be/core.rb', line 51

def enable
  return if enabled?
  
  @disabled_methods.each do |method_record|
    define_method(*method_record)
  end
  @disabled_methods = nil
  invoke_disabled_handlers
end

.enabled?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/must_be/core.rb', line 61

def enabled?
  @disabled_methods.nil?
end

.match_any_case?(v, cases) ⇒ Boolean

Returns:

  • (Boolean)


2
3
4
5
# File 'lib/must_be/basic.rb', line 2

def self.match_any_case?(v, cases)
  cases = [cases] unless cases.is_a? Array
  cases.any? {|c| c === v }
end

.must_check_member_against_cases(container, member, cases, negate = false) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/must_be/containers.rb', line 89

def self.must_check_member_against_cases(container, member, cases,
    negate = false)
  member.must_check(lambda do
    if negate
      member.must_not_be(*cases)
    else
      member.must_be(*cases)
    end
  end) do |note|
    note = ContainerNote.new(note, container)
    block_given? ? yield(note) : note
  end
end

.must_check_pair_against_hash_cases(container, key, value, cases, negate = false) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/must_be/containers.rb', line 103

def self.must_check_pair_against_hash_cases(container, key, value, cases,
    negate = false)
  unless MustBe.check_pair_against_hash_cases(key, value, cases, negate)
    note = PairNote.new(key, value, cases, container, negate)
    must_notify(block_given? ? yield(note) : note)
  end
end

.must_only_contain(container, cases, negate = false) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/must_be/containers.rb', line 111

def self.must_only_contain(container, cases, negate = false)
  prefix = negate ? "must_not_contain: " : "must_only_contain: "
  
  advice = MustOnlyEverContain.registered_class(container)    
  if advice and advice.respond_to? :must_only_contain_check
    advice.must_only_contain_check(container, cases, negate)
  elsif container.respond_to? :each_pair
    container.each_pair do |key, value|
      MustBe.must_check_pair_against_hash_cases(container, key, value,
          cases, negate) do |note|
        note.prefix = prefix
        note
      end
    end
  else
    container.each do |member|
      MustBe.must_check_member_against_cases(container, member, cases,
          negate) do |note|
        note.prefix = prefix
        note
      end
    end
  end
  container
end

.must_only_ever_contain(container, cases, negate = false) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/must_be/containers.rb', line 268

def self.must_only_ever_contain(container, cases, negate = false)
  unless container.singleton_methods.empty?
    method_name = "must_#{negate ? "never" : "only"}_ever_contain"
    raise ArgumentError, "#{method_name} adds singleton methods but"\
      " receiver #{MustBe.short_inspect(container)} already"\
      " has singleton methods #{container.singleton_methods.inspect}"
  end
  
  advice = MustOnlyEverContain.registered_class(container)
  if advice
    container.extend advice
    container.must_only_ever_contain_backtrace = caller
    container.must_only_ever_contain_negate = negate
    container.must_only_ever_contain_cases = cases
  else
    raise TypeError,
      "No MustOnlyEverContain.registered_class for #{container.class}"
  end
  container
end

.register_disabled_handler(&handler) ⇒ Object



71
72
73
74
# File 'lib/must_be/core.rb', line 71

def register_disabled_handler(&handler)
  @@disabled_handlers << handler
  handler[enabled?] unless enabled?
end

.register_disabled_method(method_name, disabled_method_name = method_name) ⇒ Object



65
66
67
68
69
# File 'lib/must_be/core.rb', line 65

def register_disabled_method(method_name,
    disabled_method_name = method_name)
  @@disabled_method_for_method[method_name.to_sym] = 
    disabled_method_name.to_sym
end

.set_notifier_from_env(key = ) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/must_be/core.rb', line 107

def set_notifier_from_env(key = ENV['MUST_BE__NOTIFIER'])
  key = key.to_sym
  
  if key == :disable
    disable
    return
  end
  
  constant_name = NOTIFIERS[key]
  unless constant_name
    raise ArgumentError, "no MustBe::NOTIFIERS called #{key.inspect}"
  end
  self.notifier = const_get(constant_name)
end

.short_inspect(obj) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/must_be/core.rb', line 9

def self.short_inspect(obj)
  s = obj.inspect
  if s.bytesize > SHORT_INSPECT_CUTOFF_LENGTH
    real_cutoff = SHORT_INSPECT_CUTOFF_LENGTH - SHORT_INSPECT_ELLIPSES.length
    left_side = (real_cutoff + 1) / 2
    right_side = -(real_cutoff / 2)
    
    left_start = left_side - SHORT_INSPECT_WORD_BREAK_LENGTH
    left_word_break_area = s[left_start, SHORT_INSPECT_WORD_BREAK_LENGTH]
    left_word_break = left_word_break_area.rindex(/\b\s/)
    start = left_word_break ? left_start + left_word_break + 1 : left_side
    
    right_start = right_side
    right_word_break_area = s[right_start, SHORT_INSPECT_WORD_BREAK_LENGTH]
    right_word_break = right_word_break_area.index(/\s\b/)
    stop = right_word_break ? right_start + right_word_break : right_side
    
    s = s.dup
    s[start...stop] = SHORT_INSPECT_ELLIPSES
  end
  s
end

Instance Method Details

#must(message = nil, &block) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/must_be/proxy.rb', line 33

def must(message = nil, &block)
  if block_given?
    unless block.arity > 1 ? yield(self, message) : yield(self)
      if message
        must_notify(message)
      else
        must_notify(self, :must, nil, block)
      end
    end
    self
  else
    Proxy.new(self, :must)
  end
end

#must_be(*cases) ⇒ Object



7
8
9
10
11
12
# File 'lib/must_be/basic.rb', line 7

def must_be(*cases)
  unless cases.empty? ? self : MustBe.match_any_case?(self, cases)
    must_notify(self, :must_be, cases, nil, ", but matches #{self.class}")
  end
  self
end

#must_be_a(*modules) ⇒ Object



43
44
45
# File 'lib/must_be/basic.rb', line 43

def must_be_a(*modules)
  must_be_a__body(modules, :none?, :must_be_a)
end

#must_be_booleanObject



87
88
89
90
91
92
# File 'lib/must_be/basic.rb', line 87

def must_be_boolean
  unless self == true or self == false
    must_notify(self, :must_be_boolean)
  end
  self
end

#must_be_close(expected, delta = 0.1) ⇒ Object



94
95
96
97
98
99
100
101
# File 'lib/must_be/basic.rb', line 94

def must_be_close(expected, delta = 0.1)
  difference = (self - expected).abs
  unless difference < delta
    must_notify(self, :must_be_close, [expected, delta], nil,
      ", difference is #{difference}")
  end
  self
end

#must_be_falseObject



82
83
84
85
# File 'lib/must_be/basic.rb', line 82

def must_be_false
  must_notify(self, :must_be_false) unless self == false
  self
end

#must_be_in(*collection) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/must_be/basic.rb', line 51

def must_be_in(*collection)
  cs = collection.size == 1 ? collection[0] : collection
  unless cs.include? self
    must_notify(self, :must_be_in, collection)
  end
  self
end

#must_be_nilObject



67
68
69
70
# File 'lib/must_be/basic.rb', line 67

def must_be_nil
  must_notify(self, :must_be_nil) unless nil?
  self
end

#must_be_trueObject



77
78
79
80
# File 'lib/must_be/basic.rb', line 77

def must_be_true
  must_notify(self, :must_be_true) unless self == true
  self
end

#must_check(check_block = nil, &block) ⇒ Object



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/must_be/core.rb', line 213

def must_check(check_block = nil, &block)
  if check_block
    result = nil
    note = must_check do |obj|
      result = check_block.arity.zero? ? check_block[] : check_block[obj]
    end
    if note
      must_notify(block[note])
    end
    return result
  end
  
  begin
    was_checking = Thread.current[:must_check__is_checking]
    Thread.current[:must_check__is_checking] = true
  
    already_found = Thread.current[:must_check__found_note]
    Thread.current[:must_check__found_note] = nil
  
    yield(self)
  
    Thread.current[:must_check__found_note]
  ensure
    Thread.current[:must_check__is_checking] = was_checking
    Thread.current[:must_check__found_note] = already_found
  end
end

#must_just_return(*args) ⇒ Object



83
84
85
# File 'lib/must_be/core.rb', line 83

def must_just_return(*args)
  self
end

#must_just_yield(*args) ⇒ Object



89
90
91
# File 'lib/must_be/core.rb', line 89

def must_just_yield(*args)
  yield
end

#must_never_ever_contain(*cases) ⇒ Object



293
294
295
# File 'lib/must_be/containers.rb', line 293

def must_never_ever_contain(*cases)
  MustBe.must_only_ever_contain(self, cases, true)
end

#must_not(message = nil, &block) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/must_be/proxy.rb', line 48

def must_not(message = nil, &block)
  if block_given?
    if block.arity > 1 ? yield(self, message) : yield(self)
      if message
        must_notify(message)
      else
        must_notify(self, :must_not, nil, block)
      end
    end
    self
  else
    Proxy.new(self, :must_not)
  end
end

#must_not_be(*cases) ⇒ Object



14
15
16
17
18
19
# File 'lib/must_be/basic.rb', line 14

def must_not_be(*cases)
  if cases.empty? ? self : MustBe.match_any_case?(self, cases)
    must_notify(self, :must_not_be, cases, nil, ", but matches #{self.class}")
  end
  self
end

#must_not_be_a(*modules) ⇒ Object



47
48
49
# File 'lib/must_be/basic.rb', line 47

def must_not_be_a(*modules)
  must_be_a__body(modules, :any?, :must_not_be_a)
end

#must_not_be_close(expected, delta = 0.1) ⇒ Object



103
104
105
106
107
108
# File 'lib/must_be/basic.rb', line 103

def must_not_be_close(expected, delta = 0.1)
  if (self - expected).abs < delta
    must_notify(self, :must_not_be_close, [expected, delta])
  end
  self
end

#must_not_be_in(*collection) ⇒ Object



59
60
61
62
63
64
65
# File 'lib/must_be/basic.rb', line 59

def must_not_be_in(*collection)
  cs = collection.size == 1 ? collection[0] : collection
  if cs.include? self
    must_notify(self, :must_not_be_in, collection)
  end
  self
end

#must_not_be_nilObject



72
73
74
75
# File 'lib/must_be/basic.rb', line 72

def must_not_be_nil
  must_notify(self, :must_not_be_nil) if nil?
  self
end

#must_not_contain(*cases) ⇒ Object



141
142
143
# File 'lib/must_be/containers.rb', line 141

def must_not_contain(*cases)
  MustBe.must_only_contain(self, cases, true)
end

#must_not_raise(*args, &block) ⇒ Object



76
77
78
# File 'lib/must_be/nonstandard_control_flow.rb', line 76

def must_not_raise(*args, &block)
  must_raise__body(:must_not_raise, args, &block)
end

#must_not_throw(*args, &block) ⇒ Object



153
154
155
# File 'lib/must_be/nonstandard_control_flow.rb', line 153

def must_not_throw(*args, &block)
  must_throw__body(:must_not_throw, args, &block)
end

#must_notify(receiver = nil, assertion = nil, args = nil, block = nil, additional_message = nil) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
# File 'lib/must_be/core.rb', line 201

def must_notify(receiver = nil, assertion= nil, args = nil, block = nil,
    additional_message = nil)
  note = Note === receiver ? receiver :
    Note.new(receiver, assertion, args, block, additional_message)
  if Thread.current[:must_check__is_checking]
    Thread.current[:must_check__found_note] = note
  else
    raise note if MustBe.notifier.call(note)
  end
  note
end

#must_only_contain(*cases) ⇒ Object



137
138
139
# File 'lib/must_be/containers.rb', line 137

def must_only_contain(*cases)
  MustBe.must_only_contain(self, cases)
end

#must_only_ever_contain(*cases) ⇒ Object



289
290
291
# File 'lib/must_be/containers.rb', line 289

def must_only_ever_contain(*cases)
  MustBe.must_only_ever_contain(self, cases)
end

#must_raise(*args, &block) ⇒ Object



72
73
74
# File 'lib/must_be/nonstandard_control_flow.rb', line 72

def must_raise(*args, &block)
  must_raise__body(:must_raise, args, &block)
end

#must_throw(*args, &block) ⇒ Object



149
150
151
# File 'lib/must_be/nonstandard_control_flow.rb', line 149

def must_throw(*args, &block)
  must_throw__body(:must_throw, args, &block)
end