rubymisc 0.0.3.3 → 0.0.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/.gitignore CHANGED
@@ -4,3 +4,4 @@ Gemfile.lock
4
4
  pkg/*
5
5
  .DS_Store
6
6
  coverage
7
+ doc
data/.irbrc ADDED
@@ -0,0 +1,16 @@
1
+ require 'pp'
2
+ require 'irb/completion'
3
+
4
+ IRB.conf[:AUTO_INDENT] = true
5
+
6
+ ##
7
+ # @example
8
+ # time { 1_000_000.times { 'word'.upcase } }
9
+ #
10
+ def time(&block)
11
+ require 'benchmark'
12
+ result = nil
13
+ timing = Benchmark.measure { result = block.() }
14
+ puts "It took: #{timing}"
15
+ result
16
+ end
data/README CHANGED
@@ -8,38 +8,47 @@
8
8
 
9
9
  2 Usage
10
10
 
11
- 2.1 Regular expressions
12
- e1: Rubymisc::Regex.email
13
- e2: Rubymisc::Regex.url
14
- e3: Rubymisc::Regex.zip
15
- e4: Rubymisc::Regex.ipv4
16
- e5: Rubymisc::Regex.mac_address
17
- e6: Rubymisc::Regex.hexcode
18
- e7: Rubymisc::Regex.usd
11
+ 2.1 Rbm::Regex
12
+
13
+ e1: puts Rbm::Regex.man
14
+ e2: Rbm::Regex.email
15
+ e3: Rbm::Regex.url
16
+ e4: Rbm::Regex.zip
17
+ e5: Rbm::Regex.ipv4
18
+ e6: Rbm::Regex.mac_address
19
+ e7: Rbm::Regex.hexcode
20
+ e8: Rbm::Regex.usd
19
21
 
20
22
  2.2 Object
21
23
 
22
24
  #in?
23
- e8: 100.in?([1, 2, 3, 4, 5, 100]) # => true
25
+ e9: 100.in? [1, 2, 3, 4, 5, 100] # => true
24
26
 
25
27
  #not functor
26
- e9: 100.not.instance_of?(Fixnum) # => false
28
+ e10: 100.not.instance_of? Fixnum # => false
29
+
30
+ #and_try functor
31
+ e11: nil.and_try.size # => nil
27
32
 
28
33
  #errors_with_message
29
- e10: begin; raise 'Timeout socket'; rescue errors_with_message(/socket/); p 'socket E'; end
34
+ e12: begin
35
+ raise 'Timeout socket'
36
+ rescue errors_with_message /socket/
37
+ p 'socket E'
38
+ end
30
39
 
31
40
  #ASSERT
32
- e11: ASSERT { fail SecurityError.new('Fail message!') }
41
+ e13: ASSERT { fail SecurityError.new('Fail message!') }
33
42
 
34
43
  2.3 Integer
35
44
 
36
45
  #percent_of
37
- e12: 50.percent_of(100) # => 50.0
46
+ e14: 50.percent_of(100) # => 50.0
38
47
 
39
48
  2.4 String
40
49
 
41
50
  #^, #xor
42
- e13: 'rubymisc'.xor 'test' # => "\u0006\u0010\u0011\r\u0019\f\u0000\u0017"
51
+ e15: 'rubymisc'.xor 'test' # => "\u0006\u0010\u0011\r\u0019\f\u0000\u0017"
43
52
 
44
53
  2.5 ActiveRecord::Base
45
54
 
data/lib/rubymisc.rb CHANGED
@@ -1,19 +1,23 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # This module encapsulates the entry point functionality to
4
+ # the extension modules.
5
+ #--
6
+ # Copyright (c) 2011 Ivan Povalyukhin.
7
+ # Licensed under the same terms as Ruby. No warranty is provided.
3
8
  module Rubymisc
9
+ class << self
10
+ def rbm_ext_load(ext_prefix = '') #:nodoc:
11
+ ->(ext_file) { require ext_prefix + ext_file }
12
+ end
13
+ private :rbm_ext_load
14
+ end
4
15
  end
5
16
 
6
- require 'rubymisc/ext/object'
7
- require 'rubymisc/ext/integer'
8
- require 'rubymisc/ext/float'
9
- require 'rubymisc/ext/string'
10
- require 'rubymisc/ext/active_record'
17
+ Dir[File.expand_path('../rubymisc/ext/*.rb', __FILE__)].each &Rubymisc.send(:rbm_ext_load)
18
+ %w{ self version regex exceptional }.each &Rubymisc.send(:rbm_ext_load, 'rubymisc/')
11
19
 
12
- require 'rubymisc/regex'
13
- require 'rubymisc/exceptional'
14
- require 'rubymisc/version'
15
-
16
- __END__
17
- ###
18
- # Copyright (C) 2011 Ivan Povalyukhin.
19
- ###
20
+ module Rbm
21
+ include Rubymisc
22
+ extend Rubymisc
23
+ end
@@ -7,15 +7,28 @@ module Rubymisc
7
7
  end
8
8
 
9
9
  def not
10
- not_functor = lambda { |op, *a, &b| !self.__send__(op, *a, &b) }
10
+ _not_functor = ->(op, *a, &b) { !self.__send__(op, *a, &b) }
11
11
 
12
- not_functor.singleton_class.module_eval <<-CODE
13
- def method_missing(method, *arguments, &block)
14
- call(method, *arguments, &block)
12
+ _not_functor.singleton_class.module_eval <<-CODE
13
+ def method_missing(method, *args, &block)
14
+ call method, *args, &block
15
15
  end
16
16
  CODE
17
17
 
18
- not_functor
18
+ _not_functor
19
+ end
20
+
21
+ def and_try
22
+ _and_try_functor = BasicObject.new
23
+ (class << _and_try_functor; self; end).module_eval do
24
+ attr_accessor :delegate_to
25
+
26
+ def method_missing(method, *args, &block)
27
+ delegate_to ? delegate_to.__send__(method, *args, &block) : delegate_to
28
+ end
29
+ end
30
+ _and_try_functor.delegate_to = self
31
+ _and_try_functor
19
32
  end
20
33
  end
21
34
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Rubymisc
4
4
  module Regex
5
- class Manual
6
- def self.man
5
+ module Manual
6
+ def man
7
7
  <<-EOS
8
8
  Ruby character representations
9
9
  ------------------------------
@@ -32,8 +32,77 @@ Ruby character classses and class-like constructs
32
32
  \\D Nondigit.
33
33
  \\s Whitespace character, [\\f\\n\\r\\t\\v].
34
34
  \\S Nonwhitespace character, [^\\f\\n\\r\\t\\v].
35
+ \\h Hexadecimal digit.
36
+ \\H Negated form of \\h.
37
+
38
+ Ruby anchors and other zero-width tests
39
+ ---------------------------------------
40
+ Sequence Meaning
41
+ ^ Start of string, or the point after any newline.
42
+ \\A Beginning of string, in all match modes.
43
+ $ End of string, or the point before any newline.
44
+ \\Z End of string, but before any final line terminator, in all match modes.
45
+ \\z End of string, in all match modes.
46
+ \\b Boundary between a \\w character and a \W character.
47
+ \\B Not-word-boundary.
48
+ \\G End of the previous match.
49
+ (?=...) Positive lookahead.
50
+ (?!...) Negative lookahead.
51
+
52
+ Ruby comments and mode modifiers
53
+ --------------------------------
54
+ Mode character Meaning
55
+ m Dot(.) matches any character, including a line terminator.
56
+ Note that this is different from most regex implementations.
57
+ x Ignore whitespace, and allow embedded comment starting with #.
58
+ i Case-insensitive match.
59
+ n Turn off wide-character processing.
60
+ o Evaluate \#{...} substitutions only once. Default is to evaluate each time the regex is evaluated.
61
+ (?imns-imns) Turn match flags on or off for the rest of pattern.
62
+ (?imns-imns: Turn match flags on or off for the rest of the subexpression.
63
+ ...)
64
+ (?#...) Treat substring as a comment.
65
+ #... Treat rest of line as a comment in /x mode.
66
+ (?<=...) Positive lookbehind.
67
+ (?<!...) Negative lookbehind.
68
+
69
+ Ruby grouping, capturing, conditional and control
70
+ -------------------------------------------------
71
+ Sequence Meaning
72
+ (...) Grouping. Submatches fill \\1, \\2, ... and $1, $2, ...
73
+ (?<name>...) Named captured. Grouped match will fill \\k<name>.
74
+ \\n In a regular expression, match what was matched by the nth earlier submatch.
75
+ $n In a replacement string, contains the nth earlier submatch.
76
+ \\k<name> In a replacement string, contains the named submatch name.
77
+ (?:...) Grouping-only parentheses, no capturing.
78
+ (?>...) Atomic grouping.
79
+ ...|... Alternation; match one or the other.
80
+ * Match 0 or more times.
81
+ + Match 1 or more times.
82
+ ? Match 1 or 0 times.
83
+ {n} Match exactly n times.
84
+ {n,} Match at least n times.
85
+ {,n} Match at most n times.
86
+ {x,y} Match at least x times, but no more than y times.
87
+ *? Match 0 or more times, but as few times as possible.
88
+ +? Match 1 or more times, but as few times as possible.
89
+ ?? Match 0 or 1 times, but as few times as possible.
90
+ {n,}? Match at least n times, but as few times as possible.
91
+ {x,y}? Match at least x times, no more than y times, and as few times as possible.
92
+
93
+ Ruby replacement sequences
94
+ --------------------------
95
+ Sequence Meaning
96
+ $1, $2, ... Captured submatches.
97
+ ${name} Matched text of a named capture group.
98
+ $' Text before match.
99
+ $& Text of match.
100
+ $` Text after match.
101
+ $+ Last parenthesized match.
102
+ $~ Match data. Encapsulates the results of a successful pattern match.
35
103
  EOS
36
104
  end
105
+ module_function :man
37
106
  end
38
107
  end
39
108
  end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ # This module encapsulates top level class methods.
4
+ module Rubymisc
5
+ class << self
6
+ def log_at_exit_error(log_to)
7
+ at_exit { dump_error_to_file(log_to) }
8
+ end
9
+
10
+ def dump_error_to_file(file)
11
+ if $!
12
+ File.open(file, 'a:utf-8:utf-8') do |log|
13
+ error = { timestamp: Time.now,
14
+ message: $!.message,
15
+ backtrace: $!.backtrace,
16
+ gems: Gem.loaded_specs.inject({}) { |m, (n, s)| m.merge(n => s.version.to_s) } }
17
+ require 'yaml'
18
+ YAML.dump(error, log)
19
+ end
20
+ end
21
+ end
22
+ private :dump_error_to_file
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Rubymisc
4
- VERSION = '0.0.3.3'
4
+ VERSION = '0.0.4'
5
5
  end
data/rubymisc.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.extra_rdoc_files = ['README']
27
27
  s.license = 'MIT'
28
28
 
29
- { rspec: '~> 2.8.0.rc1',
29
+ { rspec: '~> 2.8.0',
30
30
  rake: '~> 0.9.2.2',
31
31
  bundler: '~> 1.0.21',
32
32
  simplecov: '~> 0.5.4' }.each { |lib, v| s.add_development_dependency lib.to_s, v }
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc::Exceptional do
5
+ describe Rbm::Exceptional do
6
6
  describe '#ASSERT' do
7
7
  specify 'catches failing block and swallows the exception' do
8
8
  failing_proc = -> { fail SecurityError.new('Fail message!') }
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc::Float do
5
+ describe Rbm::Float do
6
6
  describe '#percent_of' do
7
7
  specify '50.0 is 50.0 percent of 100' do
8
8
  (50.0).percent_of(100).should be_kind_of Float
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc::Integer do
5
+ describe Rbm::Integer do
6
6
  describe '#percent_of' do
7
7
  specify '50 is 50.0 percent of 100' do
8
8
  50.percent_of(100).should be_kind_of Float
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc::Object do
5
+ describe Rbm::Object do
6
6
  let(:collection) { [1, 2, 3, 4, 5, 100] }
7
7
 
8
8
  describe '#in?' do
@@ -35,4 +35,20 @@ describe Rubymisc::Object do
35
35
  collection.not.include?(1).should be false
36
36
  end
37
37
  end
38
+
39
+ describe '#and_try Functor' do
40
+ specify 'returns nil if nil is a receiver' do
41
+ nil.should respond_to :and_try
42
+ expect { nil.and_try.size.should be nil }.not_to raise_error
43
+ end
44
+
45
+ specify 'returns false if false is a receiver' do
46
+ false.should respond_to :and_try
47
+ expect { false.and_try.size }
48
+ end
49
+
50
+ specify 'returns 11 for "test string".and_try.size' do
51
+ 'test string'.and_try.size.should be 11
52
+ end
53
+ end
38
54
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc::String do
5
+ describe Rbm::String do
6
6
  let(:test_string) { 'rubymisc' }
7
7
 
8
8
  describe '#^' do
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe 'Rubymisc::Regex::Manual' do
5
+ describe Rbm::Regex::Manual do
6
6
  specify '.man' do
7
- Rubymisc::Regex.man.should be_start_with <<-EOS
7
+ Rbm::Regex.man.should be_start_with <<-EOS
8
8
  Ruby character representations
9
9
  ------------------------------
10
10
  Sequence Meaning
@@ -21,6 +21,6 @@ Ruby character representations
21
21
  \\cchar Named control character.
22
22
  EOS
23
23
 
24
- puts "\n" + Rubymisc::Regex.man
24
+ puts "\n" + Rbm::Regex.man
25
25
  end
26
26
  end
@@ -2,66 +2,66 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc::Regex do
5
+ describe Rbm::Regex do
6
6
  describe '.email' do
7
7
  specify 'valid email regexp' do
8
- Rubymisc::Regex.email.should match 'ipoval@ya.ru'
9
- Rubymisc::Regex.email.should match 'ivan-poval@yandex.ru'
8
+ Rbm::Regex.email.should match 'ipoval@ya.ru'
9
+ Rbm::Regex.email.should match 'ivan-poval@yandex.ru'
10
10
  end
11
11
  end
12
12
 
13
13
  describe '.url' do
14
14
  specify 'valid url regexp' do
15
- Rubymisc::Regex.url.should match 'http://google.com'
16
- Rubymisc::Regex.url.should match 'http://www.google.com'
17
- Rubymisc::Regex.url.should match 'https://google.com'
18
- Rubymisc::Regex.url.should match 'https://www.google.com'
19
- Rubymisc::Regex.url.should match 'http://google.com/'
20
- Rubymisc::Regex.url.should match 'http://www.google.com/'
21
- Rubymisc::Regex.url.should match 'https://google.com/'
22
- Rubymisc::Regex.url.should match 'https://www.google.com/'
15
+ Rbm::Regex.url.should match 'http://google.com'
16
+ Rbm::Regex.url.should match 'http://www.google.com'
17
+ Rbm::Regex.url.should match 'https://google.com'
18
+ Rbm::Regex.url.should match 'https://www.google.com'
19
+ Rbm::Regex.url.should match 'http://google.com/'
20
+ Rbm::Regex.url.should match 'http://www.google.com/'
21
+ Rbm::Regex.url.should match 'https://google.com/'
22
+ Rbm::Regex.url.should match 'https://www.google.com/'
23
23
  end
24
24
  end
25
25
 
26
26
  describe '.zip' do
27
27
  specify 'valid zip regexp' do
28
- Rubymisc::Regex.zip.should match '90028'
29
- Rubymisc::Regex.zip.should match '90028-0000'
30
- Rubymisc::Regex.zip.should_not match '9002'
28
+ Rbm::Regex.zip.should match '90028'
29
+ Rbm::Regex.zip.should match '90028-0000'
30
+ Rbm::Regex.zip.should_not match '9002'
31
31
  end
32
32
  end
33
33
 
34
34
  describe '.ipv4' do
35
35
  specify 'valid dotted quad IP address' do
36
- Rubymisc::Regex.ipv4.should match '127.0.0.1'
37
- Rubymisc::Regex.ipv4.should match '224.22.5.110'
38
- Rubymisc::Regex.ipv4.should_not match '127.1'
36
+ Rbm::Regex.ipv4.should match '127.0.0.1'
37
+ Rbm::Regex.ipv4.should match '224.22.5.110'
38
+ Rbm::Regex.ipv4.should_not match '127.1'
39
39
  end
40
40
  end
41
41
 
42
42
  describe '.mac_address' do
43
43
  specify 'valid mac address' do
44
- Rubymisc::Regex.mac_address.should match '01:23:45:67:89:ab'
45
- Rubymisc::Regex.mac_address.should_not match '01:23:45'
46
- Rubymisc::Regex.mac_address.should_not match '0123456789ab'
44
+ Rbm::Regex.mac_address.should match '01:23:45:67:89:ab'
45
+ Rbm::Regex.mac_address.should_not match '01:23:45'
46
+ Rbm::Regex.mac_address.should_not match '0123456789ab'
47
47
  end
48
48
  end
49
49
 
50
50
  describe '.hexcode' do
51
51
  specify 'valid html hex code' do
52
- Rubymisc::Regex.hexcode.should match '#aaa'
53
- Rubymisc::Regex.hexcode.should match '#000AAA'
54
- Rubymisc::Regex.hexcode.should_not match 'fff'
55
- Rubymisc::Regex.hexcode.should_not match '#ggg'
52
+ Rbm::Regex.hexcode.should match '#aaa'
53
+ Rbm::Regex.hexcode.should match '#000AAA'
54
+ Rbm::Regex.hexcode.should_not match 'fff'
55
+ Rbm::Regex.hexcode.should_not match '#ggg'
56
56
  end
57
57
  end
58
58
 
59
59
  describe '.usd' do
60
60
  specify '.usd' do
61
- Rubymisc::Regex.usd.should match '$20'
62
- Rubymisc::Regex.usd.should match '$15,000.01'
63
- Rubymisc::Regex.usd.should_not match '$1.001'
64
- Rubymisc::Regex.usd.should_not match '$.99'
61
+ Rbm::Regex.usd.should match '$20'
62
+ Rbm::Regex.usd.should match '$15,000.01'
63
+ Rbm::Regex.usd.should_not match '$1.001'
64
+ Rbm::Regex.usd.should_not match '$.99'
65
65
  end
66
66
  end
67
67
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc::VERSION do
5
+ describe Rbm::VERSION do
6
6
  it { should =~ /\A\d+\.\d+\.\d+(\.\d+)?\z/ }
7
7
  end
@@ -2,5 +2,24 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Rubymisc do
5
+ describe Rbm do
6
+ describe '.log_at_exit_error' do
7
+ let(:crash_log) { 'rubymisc.crash.log' }
8
+
9
+ around(:each) { FileUtils.rm crash_log, force: true, verbose: true }
10
+
11
+ it { should respond_to :log_at_exit_error }
12
+
13
+ specify 'fail SystemExit, raised by exit to initiate the termination of the script' do
14
+ File.exist?(crash_log).should be_false
15
+
16
+ begin
17
+ fail SystemExit
18
+ rescue Exception
19
+ Rbm.send(:dump_error_to_file, crash_log)
20
+ end
21
+
22
+ File.exist?(crash_log).should be_true
23
+ end
24
+ end
6
25
  end
data/spec/spec_helper.rb CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  $:.unshift File.expand_path('../lib', __FILE__)
4
4
 
5
- # Use simplecov test coverage
6
- require 'simplecov'
5
+ require 'simplecov' # use simplecov test coverage
7
6
  SimpleCov.start
8
7
 
8
+ require 'yaml'
9
+ require 'fileutils'
9
10
  require 'rubymisc'
11
+
10
12
  Dir['./spec/support/**/*.rb'].each { |f| require f }
11
13
 
12
14
  RSpec.configure do |conf|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubymisc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-16 00:00:00.000000000 Z
12
+ date: 2012-01-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &2152009360 !ruby/object:Gem::Requirement
16
+ requirement: &2152648740 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 2.8.0.rc1
21
+ version: 2.8.0
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2152009360
24
+ version_requirements: *2152648740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &2152006660 !ruby/object:Gem::Requirement
27
+ requirement: &2152648280 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.9.2.2
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2152006660
35
+ version_requirements: *2152648280
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bundler
38
- requirement: &2152004700 !ruby/object:Gem::Requirement
38
+ requirement: &2152647820 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.0.21
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2152004700
46
+ version_requirements: *2152647820
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: simplecov
49
- requirement: &2152003480 !ruby/object:Gem::Requirement
49
+ requirement: &2152647360 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: 0.5.4
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2152003480
57
+ version_requirements: *2152647360
58
58
  description: Miscellaneous and humble tool-set of useful Ruby.
59
59
  email:
60
60
  - ipoval@ya.ru
@@ -64,6 +64,7 @@ extra_rdoc_files:
64
64
  - README
65
65
  files:
66
66
  - .gitignore
67
+ - .irbrc
67
68
  - .rvmrc
68
69
  - .travis.yml
69
70
  - Gemfile
@@ -79,6 +80,7 @@ files:
79
80
  - lib/rubymisc/ext/string.rb
80
81
  - lib/rubymisc/regex.rb
81
82
  - lib/rubymisc/regex/manual.rb
83
+ - lib/rubymisc/self.rb
82
84
  - lib/rubymisc/version.rb
83
85
  - rubymisc.gemspec
84
86
  - spec/rubymisc/exceptional_spec.rb