paul_bunyan 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.travis.yml +9 -0
  4. data/Dockerfile +13 -0
  5. data/Gemfile +6 -0
  6. data/Guardfile +16 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +51 -0
  9. data/README.rdoc +3 -0
  10. data/Rakefile +19 -0
  11. data/bin/logging_demo +17 -0
  12. data/build.sh +7 -0
  13. data/docker-compose.yml +4 -0
  14. data/lib/paul_bunyan.rb +70 -0
  15. data/lib/paul_bunyan/json_formatter.rb +122 -0
  16. data/lib/paul_bunyan/level.rb +28 -0
  17. data/lib/paul_bunyan/log_relayer.rb +148 -0
  18. data/lib/paul_bunyan/rails_ext.rb +7 -0
  19. data/lib/paul_bunyan/rails_ext/instrumentation.rb +41 -0
  20. data/lib/paul_bunyan/rails_ext/rack_logger.rb +24 -0
  21. data/lib/paul_bunyan/railtie.rb +75 -0
  22. data/lib/paul_bunyan/railtie/log_subscriber.rb +182 -0
  23. data/lib/paul_bunyan/text_formatter.rb +11 -0
  24. data/lib/paul_bunyan/version.rb +3 -0
  25. data/lib/tasks/paul_bunyan_tasks.rake +4 -0
  26. data/paul_bunyan.gemspec +30 -0
  27. data/spec/dummy/Rakefile +6 -0
  28. data/spec/dummy/app/assets/images/.keep +0 -0
  29. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  30. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  32. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  33. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  34. data/spec/dummy/app/mailers/.keep +0 -0
  35. data/spec/dummy/app/models/.keep +0 -0
  36. data/spec/dummy/app/models/concerns/.keep +0 -0
  37. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/spec/dummy/bin/bundle +3 -0
  39. data/spec/dummy/bin/rails +4 -0
  40. data/spec/dummy/bin/rake +4 -0
  41. data/spec/dummy/bin/setup +29 -0
  42. data/spec/dummy/config.ru +4 -0
  43. data/spec/dummy/config/application.rb +28 -0
  44. data/spec/dummy/config/boot.rb +5 -0
  45. data/spec/dummy/config/database.yml +25 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +41 -0
  48. data/spec/dummy/config/environments/production.rb +79 -0
  49. data/spec/dummy/config/environments/test.rb +42 -0
  50. data/spec/dummy/config/initializers/assets.rb +11 -0
  51. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  53. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/spec/dummy/config/initializers/inflections.rb +16 -0
  55. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  56. data/spec/dummy/config/initializers/secret_token.rb +1 -0
  57. data/spec/dummy/config/initializers/session_store.rb +3 -0
  58. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  59. data/spec/dummy/config/locales/en.yml +23 -0
  60. data/spec/dummy/config/routes.rb +56 -0
  61. data/spec/dummy/config/secrets.yml +22 -0
  62. data/spec/dummy/lib/assets/.keep +0 -0
  63. data/spec/dummy/log/.keep +0 -0
  64. data/spec/dummy/public/404.html +67 -0
  65. data/spec/dummy/public/422.html +67 -0
  66. data/spec/dummy/public/500.html +66 -0
  67. data/spec/dummy/public/favicon.ico +0 -0
  68. data/spec/gemfiles/40.gemfile +5 -0
  69. data/spec/gemfiles/40.gemfile.lock +137 -0
  70. data/spec/gemfiles/41.gemfile +5 -0
  71. data/spec/gemfiles/41.gemfile.lock +142 -0
  72. data/spec/gemfiles/42.gemfile +5 -0
  73. data/spec/gemfiles/42.gemfile.lock +167 -0
  74. data/spec/lib/paul_bunyan/json_formatter_spec.rb +237 -0
  75. data/spec/lib/paul_bunyan/level_spec.rb +78 -0
  76. data/spec/lib/paul_bunyan/log_relayer_spec.rb +333 -0
  77. data/spec/lib/paul_bunyan/rails_ext/instrumentation_spec.rb +81 -0
  78. data/spec/lib/paul_bunyan/railtie/log_subscriber_spec.rb +304 -0
  79. data/spec/lib/paul_bunyan/railtie_spec.rb +37 -0
  80. data/spec/lib/paul_bunyan_spec.rb +137 -0
  81. data/spec/spec_helper.rb +24 -0
  82. data/spec/support/notification_helpers.rb +22 -0
  83. metadata +303 -0
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'rails', '~> 4.1.0'
@@ -0,0 +1,142 @@
1
+ PATH
2
+ remote: ../../
3
+ specs:
4
+ logging (0.2.0)
5
+ request_store
6
+ term-ansicolor
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionmailer (4.1.12)
12
+ actionpack (= 4.1.12)
13
+ actionview (= 4.1.12)
14
+ mail (~> 2.5, >= 2.5.4)
15
+ actionpack (4.1.12)
16
+ actionview (= 4.1.12)
17
+ activesupport (= 4.1.12)
18
+ rack (~> 1.5.2)
19
+ rack-test (~> 0.6.2)
20
+ actionview (4.1.12)
21
+ activesupport (= 4.1.12)
22
+ builder (~> 3.1)
23
+ erubis (~> 2.7.0)
24
+ activemodel (4.1.12)
25
+ activesupport (= 4.1.12)
26
+ builder (~> 3.1)
27
+ activerecord (4.1.12)
28
+ activemodel (= 4.1.12)
29
+ activesupport (= 4.1.12)
30
+ arel (~> 5.0.0)
31
+ activesupport (4.1.12)
32
+ i18n (~> 0.6, >= 0.6.9)
33
+ json (~> 1.7, >= 1.7.7)
34
+ minitest (~> 5.1)
35
+ thread_safe (~> 0.1)
36
+ tzinfo (~> 1.1)
37
+ arel (5.0.1.20140414130214)
38
+ builder (3.2.2)
39
+ coderay (1.1.0)
40
+ diff-lcs (1.2.5)
41
+ erubis (2.7.0)
42
+ ffi (1.9.9)
43
+ formatador (0.2.5)
44
+ guard (2.12.7)
45
+ formatador (>= 0.2.4)
46
+ listen (>= 2.7, <= 4.0)
47
+ lumberjack (~> 1.0)
48
+ nenv (~> 0.1)
49
+ notiffany (~> 0.0)
50
+ pry (>= 0.9.12)
51
+ shellany (~> 0.0)
52
+ thor (>= 0.18.1)
53
+ guard-compat (1.2.1)
54
+ guard-rspec (4.6.0)
55
+ guard (~> 2.1)
56
+ guard-compat (~> 1.1)
57
+ rspec (>= 2.99.0, < 4.0)
58
+ i18n (0.7.0)
59
+ json (1.8.3)
60
+ listen (3.0.0)
61
+ rb-fsevent (>= 0.9.3)
62
+ rb-inotify (>= 0.9)
63
+ lumberjack (1.0.9)
64
+ mail (2.6.3)
65
+ mime-types (>= 1.16, < 3)
66
+ method_source (0.8.2)
67
+ mime-types (2.6.1)
68
+ minitest (5.7.0)
69
+ nenv (0.2.0)
70
+ notiffany (0.0.6)
71
+ nenv (~> 0.1)
72
+ shellany (~> 0.0)
73
+ pry (0.10.1)
74
+ coderay (~> 1.1.0)
75
+ method_source (~> 0.8.1)
76
+ slop (~> 3.4)
77
+ rack (1.5.5)
78
+ rack-test (0.6.3)
79
+ rack (>= 1.0)
80
+ rails (4.1.12)
81
+ actionmailer (= 4.1.12)
82
+ actionpack (= 4.1.12)
83
+ actionview (= 4.1.12)
84
+ activemodel (= 4.1.12)
85
+ activerecord (= 4.1.12)
86
+ activesupport (= 4.1.12)
87
+ bundler (>= 1.3.0, < 2.0)
88
+ railties (= 4.1.12)
89
+ sprockets-rails (~> 2.0)
90
+ railties (4.1.12)
91
+ actionpack (= 4.1.12)
92
+ activesupport (= 4.1.12)
93
+ rake (>= 0.8.7)
94
+ thor (>= 0.18.1, < 2.0)
95
+ rake (10.4.2)
96
+ rb-fsevent (0.9.5)
97
+ rb-inotify (0.9.5)
98
+ ffi (>= 0.5.0)
99
+ request_store (1.1.0)
100
+ rspec (3.3.0)
101
+ rspec-core (~> 3.3.0)
102
+ rspec-expectations (~> 3.3.0)
103
+ rspec-mocks (~> 3.3.0)
104
+ rspec-core (3.3.1)
105
+ rspec-support (~> 3.3.0)
106
+ rspec-expectations (3.3.0)
107
+ diff-lcs (>= 1.2.0, < 2.0)
108
+ rspec-support (~> 3.3.0)
109
+ rspec-mocks (3.3.1)
110
+ diff-lcs (>= 1.2.0, < 2.0)
111
+ rspec-support (~> 3.3.0)
112
+ rspec-support (3.3.0)
113
+ shellany (0.0.1)
114
+ slop (3.6.0)
115
+ sprockets (3.2.0)
116
+ rack (~> 1.0)
117
+ sprockets-rails (2.3.2)
118
+ actionpack (>= 3.0)
119
+ activesupport (>= 3.0)
120
+ sprockets (>= 2.8, < 4.0)
121
+ sqlite3 (1.3.10)
122
+ term-ansicolor (1.3.2)
123
+ tins (~> 1.0)
124
+ thor (0.19.1)
125
+ thread_safe (0.3.5)
126
+ tins (1.5.4)
127
+ tzinfo (1.2.2)
128
+ thread_safe (~> 0.1)
129
+ wwtd (0.9.1)
130
+
131
+ PLATFORMS
132
+ ruby
133
+
134
+ DEPENDENCIES
135
+ bundler (~> 1.5)
136
+ guard-rspec
137
+ logging!
138
+ rails (~> 4.1.0)
139
+ rake
140
+ rspec
141
+ sqlite3
142
+ wwtd
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'rails', '~> 4.2.0'
@@ -0,0 +1,167 @@
1
+ PATH
2
+ remote: ../../
3
+ specs:
4
+ logging (0.2.0)
5
+ request_store
6
+ term-ansicolor
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionmailer (4.2.3)
12
+ actionpack (= 4.2.3)
13
+ actionview (= 4.2.3)
14
+ activejob (= 4.2.3)
15
+ mail (~> 2.5, >= 2.5.4)
16
+ rails-dom-testing (~> 1.0, >= 1.0.5)
17
+ actionpack (4.2.3)
18
+ actionview (= 4.2.3)
19
+ activesupport (= 4.2.3)
20
+ rack (~> 1.6)
21
+ rack-test (~> 0.6.2)
22
+ rails-dom-testing (~> 1.0, >= 1.0.5)
23
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
24
+ actionview (4.2.3)
25
+ activesupport (= 4.2.3)
26
+ builder (~> 3.1)
27
+ erubis (~> 2.7.0)
28
+ rails-dom-testing (~> 1.0, >= 1.0.5)
29
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
30
+ activejob (4.2.3)
31
+ activesupport (= 4.2.3)
32
+ globalid (>= 0.3.0)
33
+ activemodel (4.2.3)
34
+ activesupport (= 4.2.3)
35
+ builder (~> 3.1)
36
+ activerecord (4.2.3)
37
+ activemodel (= 4.2.3)
38
+ activesupport (= 4.2.3)
39
+ arel (~> 6.0)
40
+ activesupport (4.2.3)
41
+ i18n (~> 0.7)
42
+ json (~> 1.7, >= 1.7.7)
43
+ minitest (~> 5.1)
44
+ thread_safe (~> 0.3, >= 0.3.4)
45
+ tzinfo (~> 1.1)
46
+ arel (6.0.0)
47
+ builder (3.2.2)
48
+ coderay (1.1.0)
49
+ diff-lcs (1.2.5)
50
+ erubis (2.7.0)
51
+ ffi (1.9.9)
52
+ formatador (0.2.5)
53
+ globalid (0.3.5)
54
+ activesupport (>= 4.1.0)
55
+ guard (2.12.7)
56
+ formatador (>= 0.2.4)
57
+ listen (>= 2.7, <= 4.0)
58
+ lumberjack (~> 1.0)
59
+ nenv (~> 0.1)
60
+ notiffany (~> 0.0)
61
+ pry (>= 0.9.12)
62
+ shellany (~> 0.0)
63
+ thor (>= 0.18.1)
64
+ guard-compat (1.2.1)
65
+ guard-rspec (4.6.0)
66
+ guard (~> 2.1)
67
+ guard-compat (~> 1.1)
68
+ rspec (>= 2.99.0, < 4.0)
69
+ i18n (0.7.0)
70
+ json (1.8.3)
71
+ listen (3.0.0)
72
+ rb-fsevent (>= 0.9.3)
73
+ rb-inotify (>= 0.9)
74
+ loofah (2.0.2)
75
+ nokogiri (>= 1.5.9)
76
+ lumberjack (1.0.9)
77
+ mail (2.6.3)
78
+ mime-types (>= 1.16, < 3)
79
+ method_source (0.8.2)
80
+ mime-types (2.6.1)
81
+ mini_portile (0.6.2)
82
+ minitest (5.7.0)
83
+ nenv (0.2.0)
84
+ nokogiri (1.6.6.2)
85
+ mini_portile (~> 0.6.0)
86
+ notiffany (0.0.6)
87
+ nenv (~> 0.1)
88
+ shellany (~> 0.0)
89
+ pry (0.10.1)
90
+ coderay (~> 1.1.0)
91
+ method_source (~> 0.8.1)
92
+ slop (~> 3.4)
93
+ rack (1.6.4)
94
+ rack-test (0.6.3)
95
+ rack (>= 1.0)
96
+ rails (4.2.3)
97
+ actionmailer (= 4.2.3)
98
+ actionpack (= 4.2.3)
99
+ actionview (= 4.2.3)
100
+ activejob (= 4.2.3)
101
+ activemodel (= 4.2.3)
102
+ activerecord (= 4.2.3)
103
+ activesupport (= 4.2.3)
104
+ bundler (>= 1.3.0, < 2.0)
105
+ railties (= 4.2.3)
106
+ sprockets-rails
107
+ rails-deprecated_sanitizer (1.0.3)
108
+ activesupport (>= 4.2.0.alpha)
109
+ rails-dom-testing (1.0.6)
110
+ activesupport (>= 4.2.0.beta, < 5.0)
111
+ nokogiri (~> 1.6.0)
112
+ rails-deprecated_sanitizer (>= 1.0.1)
113
+ rails-html-sanitizer (1.0.2)
114
+ loofah (~> 2.0)
115
+ railties (4.2.3)
116
+ actionpack (= 4.2.3)
117
+ activesupport (= 4.2.3)
118
+ rake (>= 0.8.7)
119
+ thor (>= 0.18.1, < 2.0)
120
+ rake (10.4.2)
121
+ rb-fsevent (0.9.5)
122
+ rb-inotify (0.9.5)
123
+ ffi (>= 0.5.0)
124
+ request_store (1.1.0)
125
+ rspec (3.3.0)
126
+ rspec-core (~> 3.3.0)
127
+ rspec-expectations (~> 3.3.0)
128
+ rspec-mocks (~> 3.3.0)
129
+ rspec-core (3.3.1)
130
+ rspec-support (~> 3.3.0)
131
+ rspec-expectations (3.3.0)
132
+ diff-lcs (>= 1.2.0, < 2.0)
133
+ rspec-support (~> 3.3.0)
134
+ rspec-mocks (3.3.1)
135
+ diff-lcs (>= 1.2.0, < 2.0)
136
+ rspec-support (~> 3.3.0)
137
+ rspec-support (3.3.0)
138
+ shellany (0.0.1)
139
+ slop (3.6.0)
140
+ sprockets (3.2.0)
141
+ rack (~> 1.0)
142
+ sprockets-rails (2.3.2)
143
+ actionpack (>= 3.0)
144
+ activesupport (>= 3.0)
145
+ sprockets (>= 2.8, < 4.0)
146
+ sqlite3 (1.3.10)
147
+ term-ansicolor (1.3.2)
148
+ tins (~> 1.0)
149
+ thor (0.19.1)
150
+ thread_safe (0.3.5)
151
+ tins (1.5.4)
152
+ tzinfo (1.2.2)
153
+ thread_safe (~> 0.1)
154
+ wwtd (0.9.1)
155
+
156
+ PLATFORMS
157
+ ruby
158
+
159
+ DEPENDENCIES
160
+ bundler (~> 1.5)
161
+ guard-rspec
162
+ logging!
163
+ rails (~> 4.2.0)
164
+ rake
165
+ rspec
166
+ sqlite3
167
+ wwtd
@@ -0,0 +1,237 @@
1
+ require "spec_helper"
2
+ require "time"
3
+
4
+ module PaulBunyan
5
+ describe JSONFormatter do
6
+ let(:formatter) { JSONFormatter.new }
7
+ let(:time) { Time.new(2015, 2, 7, 13, 52, 3.141592) }
8
+
9
+ describe "#call(severity, time, progname, msg)" do
10
+ it "must return an object seralized as JSON" do
11
+ output = formatter.call('', time, '', '')
12
+ expect(JSON.parse(output)).to be_a Hash
13
+ end
14
+
15
+ it "must include the supplied severity string in the output" do
16
+ output = formatter.call('WARN', time, '', '')
17
+ object = JSON.parse(output)
18
+ expect(object['severity']).to eq 'WARN'
19
+ end
20
+
21
+ it "must include the supplied timestamp including miliseconds" do
22
+ output = formatter.call('', time, '', '')
23
+ object = JSON.parse(output)
24
+ expect(object['ts']).to eq time.strftime('%Y-%m-%dT%H:%M:%S.%3N')
25
+ end
26
+
27
+ it "must include a high resolution unix timestamp in as the unix_ts key" do
28
+ output = formatter.call('', time, '', '')
29
+ object = JSON.parse(output)
30
+ expect(object['unix_ts']).to eq time.to_f
31
+ end
32
+
33
+ it "must include the supplied progname argument as the program key" do
34
+ output = formatter.call('', time, 'FooBar', '')
35
+ object = JSON.parse(output)
36
+ expect(object['program']).to eq 'FooBar'
37
+ end
38
+
39
+ it "must include the current process id as the pid key" do
40
+ output = formatter.call('', time, '', '')
41
+ object = JSON.parse(output)
42
+ expect(object['pid']).to eq $$
43
+ end
44
+
45
+ it 'must not include the tags array when none are set' do
46
+ output = formatter.call('', time, '', 'This is my message, there are many like it.')
47
+ object = JSON.parse(output)
48
+ expect(object['tags']).to be_nil
49
+ end
50
+
51
+ it 'must include the tags array when some are set' do
52
+ formatter.tagged(%w{foo bar}) do
53
+ output = formatter.call('', time, '', 'This is my message, there are many like it.')
54
+ object = JSON.parse(output)
55
+ expect(object['tags']).to eq %w{foo bar}
56
+ end
57
+ end
58
+
59
+ context 'when supplied a string as the message' do
60
+ it 'must wrap the message in an object with a message key containing the key' do
61
+ output = formatter.call('', time, '', 'This is my message, there are many like it.')
62
+ object = JSON.parse(output)
63
+ expect(object['message']).to eq 'This is my message, there are many like it.'
64
+ end
65
+
66
+ it 'must remove leading whitespace' do
67
+ output = formatter.call('', time, '', ' this message has leading spaces')
68
+ object = JSON.parse(output)
69
+ expect(object['message']).to eq 'this message has leading spaces'
70
+ end
71
+
72
+ it 'must remove trailing whitespace' do
73
+ output = formatter.call('', time, '', 'this message has trailing spaces ')
74
+ object = JSON.parse(output)
75
+ expect(object['message']).to eq 'this message has trailing spaces'
76
+ end
77
+
78
+ it 'must remove ANSI color codes' do
79
+ output = formatter.call('', time, '', "\e[36;46mcolored message!")
80
+ object = JSON.parse(output)
81
+ expect(object['message']).to eq 'colored message!'
82
+ end
83
+ end
84
+
85
+ context 'when supplied a Hash' do
86
+ let(:logged_hash) {
87
+ {
88
+ foo: 'bar',
89
+ ts: 'baz',
90
+ }
91
+ }
92
+
93
+ let(:output) { formatter.call('', time, '', logged_hash) }
94
+ let(:parsed_output) { JSON.parse(output) }
95
+
96
+ it "must merge the hash with the base information" do
97
+ expect(parsed_output['foo']).to eq 'bar'
98
+ end
99
+
100
+ it "must protect the base attributes by prefixing 'user' to supplied keys that collide" do
101
+ expect(parsed_output['user.ts']).to_not be nil
102
+ end
103
+ end
104
+
105
+ context "when supplied an exception object" do
106
+ let(:exception) {
107
+ begin raise StandardError, "This is my exception...."
108
+ rescue; exception = $!; end
109
+ exception
110
+ }
111
+ let(:output) { formatter.call('', time, '', exception) }
112
+ let(:parsed_output) { JSON.parse(output) }
113
+
114
+ it "must include the exception class" do
115
+ expect(parsed_output['exception.class']).to eq 'StandardError'
116
+ end
117
+
118
+ it "must include the exception's message" do
119
+ expect(parsed_output['exception.message']).to eq 'This is my exception....'
120
+ end
121
+
122
+ it "must include the exception's backtrace" do
123
+ expect(parsed_output['exception.backtrace']).to eq exception.backtrace
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#clear_tags!' do
129
+ it 'must not fail when no tags have been set' do
130
+ formatter.clear_tags!
131
+ end
132
+
133
+ it 'must remove all tags that have been set' do
134
+ formatter.current_tags << 'foo'
135
+ expect(formatter.current_tags).to_not be_empty
136
+ formatter.clear_tags!
137
+ expect(formatter.current_tags).to be_empty
138
+ end
139
+ end
140
+
141
+ describe '#pop_tags(count = 1)' do
142
+ before do
143
+ formatter.push_tags %w{foo bar baz qux}
144
+ end
145
+
146
+ after do
147
+ formatter.clear_tags!
148
+ end
149
+
150
+ it 'must default to removing the last tag added' do
151
+ formatter.pop_tags
152
+ expect(formatter.current_tags).to_not include 'qux'
153
+ end
154
+
155
+ it 'must pop the specified number of tags' do
156
+ formatter.pop_tags(3)
157
+ expect(formatter.current_tags).to eq %w{foo}
158
+ end
159
+ end
160
+
161
+ describe '#push_tags(*tags)' do
162
+ after do
163
+ formatter.clear_tags!
164
+ end
165
+
166
+ it 'must add a single tag to the tags array' do
167
+ formatter.push_tags('foo')
168
+ expect(formatter.current_tags).to include 'foo'
169
+ end
170
+
171
+ it 'must add multiple tags to the current tags array' do
172
+ formatter.push_tags 'foo', 'bar', :baz
173
+ expect(formatter.current_tags).to contain_exactly 'foo', 'bar', :baz
174
+ end
175
+
176
+ it 'must add an array of tags to the current tags array' do
177
+ formatter.push_tags %w{foo bar baz}
178
+ expect(formatter.current_tags).to contain_exactly 'foo', 'bar', 'baz'
179
+ end
180
+
181
+ it 'must reject any tags that are nil' do
182
+ formatter.push_tags('foo', 'bar', nil, 'qux')
183
+ expect(formatter.current_tags).to contain_exactly 'foo', 'bar', 'qux'
184
+ end
185
+
186
+ it 'must reject any tags that are empty strings' do
187
+ formatter.push_tags('foo', '', 'bar')
188
+ expect(formatter.current_tags).to contain_exactly 'foo', 'bar'
189
+ end
190
+ end
191
+
192
+ describe '#tagged(*tags)' do
193
+ after do
194
+ formatter.clear_tags!
195
+ end
196
+
197
+ it 'must yeild to the supplied block' do
198
+ block_called = false
199
+ formatter.tagged(%w{foo bar}) do
200
+ block_called = true
201
+ end
202
+ expect(block_called).to eq true
203
+ end
204
+
205
+ it 'must set add the passed tags to the current tags array' do
206
+ formatter.tagged(%w{bar baz}) do
207
+ expect(formatter.current_tags).to eq %w{bar baz}
208
+ end
209
+ end
210
+
211
+ it 'must remove the set tags after the block has executed' do
212
+ formatter.tagged(%w{bar baz}) do
213
+ # nop
214
+ end
215
+ expect(formatter.current_tags).to be_empty
216
+ end
217
+
218
+ it 'must not remove tags set before the call to tagged' do
219
+ formatter.push_tags(%w{foo bar})
220
+ formatter.tagged(%w{baz qux}) do
221
+ expect(formatter.current_tags).to eq %w{foo bar baz qux}
222
+ end
223
+ expect(formatter.current_tags).to eq %w{foo bar}
224
+ end
225
+
226
+ it 'must remove the added tags even when an exception is raised in the block' do
227
+ begin
228
+ formatter.tagged(%w{baz qux}) do
229
+ raise 'oh noes!'
230
+ end
231
+ rescue
232
+ end
233
+ expect(formatter.current_tags).to be_empty
234
+ end
235
+ end
236
+ end
237
+ end