paul_bunyan 1.0.0

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.
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