gmail 0.6.0 → 0.7.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +27 -27
  4. data/.rspec +1 -1
  5. data/.rubocop.yml +53 -13
  6. data/.rubocop_todo.yml +299 -239
  7. data/.travis.yml +19 -19
  8. data/CHANGELOG.md +152 -145
  9. data/Gemfile +5 -3
  10. data/LICENSE +21 -21
  11. data/README.md +380 -355
  12. data/Rakefile +44 -46
  13. data/gmail.gemspec +32 -34
  14. data/lib/gmail.rb +72 -78
  15. data/lib/gmail/client.rb +35 -34
  16. data/lib/gmail/client/base.rb +244 -229
  17. data/lib/gmail/client/plain.rb +24 -24
  18. data/lib/gmail/client/xoauth.rb +67 -68
  19. data/lib/gmail/client/xoauth2.rb +39 -39
  20. data/lib/gmail/imap_extensions.rb +156 -159
  21. data/lib/gmail/labels.rb +80 -79
  22. data/lib/gmail/mailbox.rb +178 -175
  23. data/lib/gmail/message.rb +212 -207
  24. data/lib/gmail/version.rb +3 -3
  25. data/spec/account.yml.example +1 -1
  26. data/spec/account.yml.obfus +2 -2
  27. data/spec/gmail/client/base_spec.rb +5 -5
  28. data/spec/gmail/client/plain_spec.rb +169 -169
  29. data/spec/gmail/client/xoauth2_spec.rb +186 -186
  30. data/spec/gmail/client/xoauth_spec.rb +5 -5
  31. data/spec/gmail/client_spec.rb +5 -5
  32. data/spec/gmail/imap_extensions_spec.rb +47 -47
  33. data/spec/gmail/labels_spec.rb +27 -27
  34. data/spec/gmail/mailbox_spec.rb +84 -84
  35. data/spec/gmail/message_spec.rb +181 -181
  36. data/spec/gmail_spec.rb +40 -39
  37. data/spec/recordings/gmail/_new_connects_with_client_and_give_it_context_when_block_given.yml +61 -28
  38. data/spec/recordings/gmail/_new_connects_with_gmail_service_and_return_valid_connection_object.yml +43 -28
  39. data/spec/recordings/gmail/_new_does_not_raise_error_when_couldn_t_connect_with_given_account.yml +21 -13
  40. data/spec/recordings/gmail/_new_raises_error_when_couldn_t_connect_with_given_account.yml +21 -13
  41. data/spec/recordings/gmail_client_plain/instance/delivers_inline_composed_email.yml +61 -42
  42. data/spec/recordings/gmail_client_plain/instance/does_not_log_in_when_given_gmail_account_is_invalid.yml +21 -13
  43. data/spec/recordings/gmail_client_plain/instance/does_not_raise_error_even_though_gmail_account_is_invalid.yml +21 -13
  44. data/spec/recordings/gmail_client_plain/instance/labels/checks_if_there_is_given_label_defined.yml +409 -196
  45. data/spec/recordings/gmail_client_plain/instance/labels/creates_given_label.yml +280 -151
  46. data/spec/recordings/gmail_client_plain/instance/labels/removes_existing_label.yml +271 -146
  47. data/spec/recordings/gmail_client_plain/instance/labels/returns_list_of_all_available_labels.yml +227 -113
  48. data/spec/recordings/gmail_client_plain/instance/properly_logs_in_to_valid_gmail_account.yml +61 -42
  49. data/spec/recordings/gmail_client_plain/instance/properly_logs_out_from_gmail.yml +61 -42
  50. data/spec/recordings/gmail_client_plain/instance/properly_switches_to_given_mailbox.yml +164 -109
  51. data/spec/recordings/gmail_client_plain/instance/properly_switches_to_given_mailbox_using_block_style.yml +164 -109
  52. data/spec/recordings/gmail_client_plain/instance/raises_error_when_given_gmail_account_is_invalid_and_errors_enabled.yml +21 -13
  53. data/spec/recordings/gmail_client_xo_auth2/instance/does_not_log_in_when_given_gmail_account_is_invalid.yml +24 -13
  54. data/spec/recordings/gmail_client_xo_auth2/instance/labels/checks_if_there_is_given_label_defined.yml +39 -27
  55. data/spec/recordings/gmail_client_xo_auth2/instance/labels/creates_given_label.yml +52 -39
  56. data/spec/recordings/gmail_client_xo_auth2/instance/labels/removes_existing_label.yml +52 -39
  57. data/spec/recordings/gmail_client_xo_auth2/instance/labels/returns_list_of_all_available_labels.yml +39 -27
  58. data/spec/recordings/gmail_client_xo_auth2/instance/properly_logs_in_to_valid_gmail_account.yml +26 -15
  59. data/spec/recordings/gmail_client_xo_auth2/instance/properly_logs_out_from_gmail.yml +26 -15
  60. data/spec/recordings/gmail_client_xo_auth2/instance/properly_switches_to_given_mailbox.yml +63 -40
  61. data/spec/recordings/gmail_client_xo_auth2/instance/properly_switches_to_given_mailbox_using_block_style.yml +63 -40
  62. data/spec/recordings/gmail_client_xo_auth2/instance/raises_error_when_given_gmail_account_is_invalid_and_errors_enabled.yml +24 -13
  63. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/all/localizes_into_the_appropriate_label.yml +229 -116
  64. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/and_the_mailbox_does_not_exist/returns_the_mailbox_name_as_a_string.yml +229 -110
  65. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/drafts/localizes_into_the_appropriate_label.yml +229 -116
  66. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/flagged/localizes_into_the_appropriate_label.yml +229 -116
  67. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/important/localizes_into_the_appropriate_label.yml +229 -116
  68. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/inbox/localizes_into_the_appropriate_label.yml +61 -42
  69. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/junk/localizes_into_the_appropriate_label.yml +229 -116
  70. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/sent/localizes_into_the_appropriate_label.yml +229 -116
  71. data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/trash/localizes_into_the_appropriate_label.yml +229 -116
  72. data/spec/recordings/gmail_mailbox/instance/counts_all_emails.yml +595 -277
  73. data/spec/recordings/gmail_mailbox/instance/finds_messages.yml +1049 -586
  74. data/spec/recordings/gmail_mailbox/instance/waits_once.yml +191 -136
  75. data/spec/recordings/gmail_mailbox/instance/waits_repeatedly.yml +217 -141
  76. data/spec/recordings/gmail_mailbox/instance/waits_with_29-minute_re-issue.yml +188 -136
  77. data/spec/recordings/gmail_mailbox/instance/waits_with_an_unblocked_connection.yml +644 -207
  78. data/spec/recordings/gmail_mailbox/on_initialize/sets_client_and_name.yml +61 -42
  79. data/spec/recordings/gmail_mailbox/on_initialize/works_in_inbox_by_default.yml +61 -42
  80. data/spec/recordings/gmail_message/initialize/sets_prefetch_attrs.yml +1068 -578
  81. data/spec/recordings/gmail_message/initialize/sets_uid_and_mailbox.yml +1068 -580
  82. data/spec/recordings/gmail_message/instance_methods/deletes_itself.yml +1084 -637
  83. data/spec/recordings/gmail_message/instance_methods/marks_itself_read.yml +1137 -682
  84. data/spec/recordings/gmail_message/instance_methods/marks_itself_unread.yml +1153 -686
  85. data/spec/recordings/gmail_message/instance_methods/moves_from_one_tag_to_other.yml +1447 -862
  86. data/spec/recordings/gmail_message/instance_methods/removes_a_given_label.yml +1288 -776
  87. data/spec/recordings/gmail_message/instance_methods/removes_a_given_label_with_old_method.yml +1288 -776
  88. data/spec/recordings/gmail_message/instance_methods/sets_given_label.yml +1165 -690
  89. data/spec/recordings/gmail_message/instance_methods/sets_given_label_with_old_method.yml +1157 -691
  90. data/spec/spec_helper.rb +56 -53
  91. data/spec/support/imap_mock.rb +181 -181
  92. data/spec/support/obfuscation.rb +49 -52
  93. metadata +21 -90
  94. data/spec/recordings/gmail_client_plain/instance/_connection_automatically_logs_in_to_gmail_account_when_it_s_called.yml +0 -42
@@ -1,53 +1,56 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
- require 'rubygems'
4
- require 'rspec'
5
- require 'yaml'
6
- require 'gmail'
7
-
8
- # require_support_files
9
- Dir[File.join(File.dirname(__FILE__), 'support', '*.rb')].each { |f| require f }
10
-
11
- RSpec.configure do |config|
12
- Spec::ImapMock.configure_rspec!(config)
13
- end
14
-
15
- def within_gmail(&block)
16
- Gmail.connect!(*TEST_ACCOUNT) do |gmail|
17
- yield(gmail)
18
- end
19
- end
20
-
21
- def mock_client(&block)
22
- client = Gmail::Client::Plain.new(*TEST_ACCOUNT)
23
- client.connect
24
-
25
- if block_given?
26
- client.login
27
- yield client
28
- client.logout
29
- end
30
-
31
- client
32
- end
33
-
34
- def mock_mailbox(box = "INBOX", &block)
35
- within_gmail do |gmail|
36
- mailbox = gmail.mailbox(box)
37
- yield(mailbox) if block_given?
38
- mailbox
39
- end
40
- end
41
-
42
- # TODO: move this to it's own dir; get rid of global variable
43
- # Run test by creating your own test account with credentials in account.yml
44
- # Otherwise default credentials from an obfuscated file are used.
45
- clear_file = File.join(File.dirname(__FILE__), 'account.yml')
46
- obfus_file = File.join(File.dirname(__FILE__), 'account.yml.obfus')
47
- if File.exist?(clear_file)
48
- TEST_ACCOUNT = YAML.load_file(clear_file)
49
- elsif File.exist?(obfus_file)
50
- TEST_ACCOUNT = Spec::Obfuscation.decrypt_file(obfus_file)
51
- else
52
- raise 'account.yml file not found'
53
- end
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'rubygems'
4
+ require 'rspec'
5
+ require 'yaml'
6
+ require 'gmail'
7
+ require 'coveralls'
8
+
9
+ Coveralls.wear!
10
+
11
+ # require_support_files
12
+ Dir[File.join(File.dirname(__FILE__), 'support', '*.rb')].each { |f| require f }
13
+
14
+ RSpec.configure do |config|
15
+ Spec::ImapMock.configure_rspec!(config)
16
+ end
17
+
18
+ def within_gmail(&block)
19
+ Gmail.connect!(*TEST_ACCOUNT) do |gmail|
20
+ yield(gmail)
21
+ end
22
+ end
23
+
24
+ def mock_client(&block)
25
+ client = Gmail::Client::Plain.new(*TEST_ACCOUNT)
26
+ client.connect
27
+
28
+ if block_given?
29
+ client.login
30
+ yield client
31
+ client.logout
32
+ end
33
+
34
+ client
35
+ end
36
+
37
+ def mock_mailbox(box = "INBOX", &block)
38
+ within_gmail do |gmail|
39
+ mailbox = gmail.mailbox(box)
40
+ yield(mailbox) if block_given?
41
+ mailbox
42
+ end
43
+ end
44
+
45
+ # TODO: move this to it's own dir; get rid of global variable
46
+ # Run test by creating your own test account with credentials in account.yml
47
+ # Otherwise default credentials from an obfuscated file are used.
48
+ clear_file = File.join(File.dirname(__FILE__), 'account.yml')
49
+ obfus_file = File.join(File.dirname(__FILE__), 'account.yml.obfus')
50
+ if File.exist?(clear_file)
51
+ TEST_ACCOUNT = YAML.load_file(clear_file)
52
+ elsif File.exist?(obfus_file)
53
+ TEST_ACCOUNT = Spec::Obfuscation.decrypt_file(obfus_file)
54
+ else
55
+ raise 'account.yml file not found'
56
+ end
@@ -1,181 +1,181 @@
1
- module Net
2
- class IMAP
3
- class << self
4
- def recordings=(value)
5
- @replaying = !value.nil?
6
- @recordings = value
7
- end
8
-
9
- def recordings
10
- @recordings ||= {}
11
- end
12
-
13
- def replaying?
14
- @replaying
15
- end
16
- end
17
-
18
- alias_method :_idle, :idle
19
- alias_method :_idle_done, :idle_done
20
-
21
- def idle(&response_handler)
22
- if Net::IMAP.replaying?
23
- @idle_done_cond = new_cond
24
- @idle_done = false
25
- end
26
-
27
- response = mock_command(:_idle, 'IDLE', &response_handler)
28
-
29
- if Net::IMAP.replaying?
30
- synchronize do
31
- unless @idle_done
32
- @idle_done_cond.wait(0.1)
33
- raise('The IDLE has not done') unless @idle_done
34
- end
35
- @idle_done_cond = nil
36
- end
37
- end
38
-
39
- response
40
- end
41
-
42
- def idle_done
43
- if Net::IMAP.replaying?
44
- synchronize do
45
- if @idle_done_cond.nil?
46
- raise Net::IMAP::Error, 'not during IDLE'
47
- end
48
- @idle_done = true
49
- idle_done_cond.signal
50
- end
51
- else
52
- _idle_done
53
- end
54
- end
55
-
56
- private
57
-
58
- alias_method :_send_command, :send_command
59
-
60
- def self.force_utf8(data)
61
- case data.class.to_s
62
- when /String/
63
- data.force_encoding('utf-8')
64
- when /Hash/
65
- data.each { |k, v| data[k] = force_utf8(v) }
66
- when /Array/
67
- data.map { |s| force_utf8(s) }
68
- end
69
- end
70
-
71
- def send_command(cmd, *args, &block)
72
- mock_command(:_send_command, cmd, *args, &block)
73
- end
74
-
75
- def mock_command(method, cmd, *args, &block)
76
- # In Ruby 1.9.x, strings default to binary which causes the digest to be
77
- # different.
78
- clean_args = args.dup.each do |s|
79
- Net::IMAP.force_utf8(s)
80
- end
81
-
82
- yaml_dump = YAML.dump([cmd] + clean_args)
83
-
84
- if RUBY_VERSION =~ /^(1.9|2.0)/
85
- # From 1.9 to 2.0 to 2.1, the way YAML encodes special characters changed.
86
- # Here's what each returns for: YAML.dump(["", "%"])
87
- # 1.9.x: "---\n- ''\n- ! '%'\n"
88
- # 2.0.x: "---\n- ''\n- '%'\n"
89
- # 2.1.x: "---\n- ''\n- \"%\"\n"
90
- # The `gsub` here converts the older format into the 2.1.x.
91
- yaml_dump.gsub!(/(?:! )?'(.+)'/, '"\1"')
92
-
93
- # In 1.9 and 2.0 strings starting with `+` or `-` are not escaped in quotes, but
94
- # they are in 2.1+. This addresses that.
95
- yaml_dump.gsub!(/ ([+-](?:X-GM-\w+|FLAGS))/, ' "\1"')
96
-
97
- # In 1.9 and 2.0 strings starting with `\` are not escaped in quotes, but
98
- # they are in 2.1+. This addresses that. Yes we need all those backslashes :|
99
- yaml_dump.gsub!(/ \\(\w+)/, ' "\\\\\\\\\1"')
100
- end
101
-
102
- digest = "#{cmd}-#{Digest::MD5.hexdigest(yaml_dump)}"
103
-
104
- if Net::IMAP.replaying?
105
- recordings = Net::IMAP.recordings[digest] || []
106
- if recordings.empty?
107
- # Be lenient if LOGOUT is called but wasn't explicitly recorded. This
108
- # comes up often when called from `at_exit`.
109
- cmd == 'LOGOUT' ? return : raise('Could not find recording')
110
- end
111
-
112
- action, response, @responses, all_responses = recordings.shift
113
-
114
- if block && all_responses
115
- all_responses.each do |resp|
116
- block.call(resp)
117
- end
118
- end
119
- else
120
- action = :return
121
- all_responses = []
122
- begin
123
- response = send(method, cmd, *args) do |resp|
124
- all_responses << resp
125
- block.call(resp)
126
- end
127
- rescue => e
128
- action = :raise
129
- response = e
130
- end
131
-
132
- # @responses (the third argument here) contains untagged responses captured
133
- # via the Net::IMAP#record_response method.
134
- Net::IMAP.recordings[digest] ||= []
135
- Net::IMAP.recordings[digest] << [action, response.dup, @responses ? @responses.dup : nil, all_responses]
136
- end
137
-
138
- raise(response) if action == :raise
139
-
140
- response
141
- end
142
- end
143
- end
144
-
145
- module Spec
146
- module ImapMock
147
- # Configures RSpec with an around(:each) block to use IMAP mocks
148
- def self.configure_rspec!(config)
149
- config.around(:each) do |example|
150
- Spec::ImapMock.run_rspec_example(example)
151
- end
152
- end
153
-
154
- # Run an RSpec example using IMAP mocks
155
- def self.run_rspec_example(example)
156
- # The path is determined by the rspec `describe`s and `context`s
157
- mock_path = example.example_group.to_s
158
- .gsub(/RSpec::ExampleGroups::/, '')
159
- .gsub(/(\w)([A-Z])/, '\1_\2')
160
- .gsub(/::/, '/')
161
- .downcase
162
-
163
- # The name is determined by the description of the example.
164
- mock_name = example.description.gsub(/[^\w\-\/]+/, '_').downcase
165
-
166
- filename = File.join('spec/recordings/', mock_path, "#{mock_name}.yml")
167
-
168
- # If we've already recorded this spec load the recordings
169
- Net::IMAP.recordings = File.exist?(filename) ? YAML.load_file(filename) : nil
170
-
171
- example.run
172
-
173
- # If we haven't yet recorded the spec and there were some recordings,
174
- # write them to a file.
175
- unless File.exist?(filename) or Net::IMAP.recordings.empty?
176
- FileUtils.mkdir_p(File.dirname(filename))
177
- File.open(filename, 'w') { |f| YAML.dump(Net::IMAP.recordings, f) }
178
- end
179
- end
180
- end
181
- end
1
+ module Net
2
+ class IMAP
3
+ class << self
4
+ def recordings=(value)
5
+ @replaying = !value.nil?
6
+ @recordings = value
7
+ end
8
+
9
+ def recordings
10
+ @recordings ||= {}
11
+ end
12
+
13
+ def replaying?
14
+ @replaying
15
+ end
16
+
17
+ def force_utf8(data)
18
+ case data.class.to_s
19
+ when /String/
20
+ data.force_encoding('utf-8')
21
+ when /Hash/
22
+ data.each { |k, v| data[k] = force_utf8(v) }
23
+ when /Array/
24
+ data.map { |s| force_utf8(s) }
25
+ end
26
+ end
27
+ end
28
+
29
+ alias_method :_idle, :idle
30
+ alias_method :_idle_done, :idle_done
31
+
32
+ def idle(&response_handler)
33
+ if Net::IMAP.replaying?
34
+ @idle_done_cond = new_cond
35
+ @idle_done = false
36
+ end
37
+
38
+ response = mock_command(:_idle, 'IDLE', &response_handler)
39
+
40
+ if Net::IMAP.replaying?
41
+ synchronize do
42
+ unless @idle_done
43
+ @idle_done_cond.wait(0.1)
44
+ raise('The IDLE has not done') unless @idle_done
45
+ end
46
+ @idle_done_cond = nil
47
+ end
48
+ end
49
+
50
+ response
51
+ end
52
+
53
+ def idle_done
54
+ if Net::IMAP.replaying?
55
+ synchronize do
56
+ if @idle_done_cond.nil?
57
+ raise Net::IMAP::Error, 'not during IDLE'
58
+ end
59
+ @idle_done = true
60
+ idle_done_cond.signal
61
+ end
62
+ else
63
+ _idle_done
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ alias_method :_send_command, :send_command
70
+
71
+ def send_command(cmd, *args, &block)
72
+ mock_command(:_send_command, cmd, *args, &block)
73
+ end
74
+
75
+ def mock_command(method, cmd, *args, &block)
76
+ # In Ruby 1.9.x, strings default to binary which causes the digest to be
77
+ # different.
78
+ clean_args = args.dup.each do |s|
79
+ Net::IMAP.force_utf8(s)
80
+ end
81
+
82
+ yaml_dump = YAML.dump([cmd] + clean_args)
83
+
84
+ if RUBY_VERSION =~ /^(1.9|2.0)/
85
+ # From 1.9 to 2.0 to 2.1, the way YAML encodes special characters changed.
86
+ # Here's what each returns for: YAML.dump(["", "%"])
87
+ # 1.9.x: "---\n- ''\n- ! '%'\n"
88
+ # 2.0.x: "---\n- ''\n- '%'\n"
89
+ # 2.1.x: "---\n- ''\n- \"%\"\n"
90
+ # The `gsub` here converts the older format into the 2.1.x.
91
+ yaml_dump.gsub!(/(?:! )?'(.+)'/, '"\1"')
92
+
93
+ # In 1.9 and 2.0 strings starting with `+` or `-` are not escaped in quotes, but
94
+ # they are in 2.1+. This addresses that.
95
+ yaml_dump.gsub!(/ ([+-](?:X-GM-\w+|FLAGS))/, ' "\1"')
96
+
97
+ # In 1.9 and 2.0 strings starting with `\` are not escaped in quotes, but
98
+ # they are in 2.1+. This addresses that. Yes we need all those backslashes :|
99
+ yaml_dump.gsub!(/ \\(\w+)/, ' "\\\\\\\\\1"')
100
+ end
101
+
102
+ digest = "#{cmd}-#{Digest::MD5.hexdigest(yaml_dump)}"
103
+
104
+ if Net::IMAP.replaying?
105
+ recordings = Net::IMAP.recordings[digest] || []
106
+ if recordings.empty?
107
+ # Be lenient if LOGOUT is called but wasn't explicitly recorded. This
108
+ # comes up often when called from `at_exit`.
109
+ cmd == 'LOGOUT' ? return : raise('Could not find recording')
110
+ end
111
+
112
+ action, response, @responses, all_responses = recordings.shift
113
+
114
+ if block && all_responses
115
+ all_responses.each do |resp|
116
+ yield(resp)
117
+ end
118
+ end
119
+ else
120
+ action = :return
121
+ all_responses = []
122
+ begin
123
+ args.unshift(cmd) if method==:_send_command
124
+ response = send(method, *args) do |resp|
125
+ all_responses << resp
126
+ yield(resp)
127
+ end
128
+ rescue StandardError => e
129
+ action = :raise
130
+ response = e
131
+ end
132
+
133
+ # @responses (the third argument here) contains untagged responses captured
134
+ # via the Net::IMAP#record_response method.
135
+ Net::IMAP.recordings[digest] ||= []
136
+ Net::IMAP.recordings[digest] << [action, response.dup, @responses ? @responses.dup : nil, all_responses]
137
+ end
138
+
139
+ raise(response) if action == :raise
140
+
141
+ response
142
+ end
143
+ end
144
+ end
145
+
146
+ module Spec
147
+ module ImapMock
148
+ # Configures RSpec with an around(:each) block to use IMAP mocks
149
+ def self.configure_rspec!(config)
150
+ config.around(:each) do |example|
151
+ Spec::ImapMock.run_rspec_example(example)
152
+ end
153
+ end
154
+
155
+ # Run an RSpec example using IMAP mocks
156
+ def self.run_rspec_example(example)
157
+ # The path is determined by the rspec `describe`s and `context`s
158
+ mock_path = example.example_group.to_s
159
+ .gsub(/RSpec::ExampleGroups::/, '')
160
+ .gsub(/(\w)([A-Z])/, '\1_\2')
161
+ .gsub(/::/, '/')
162
+ .downcase
163
+
164
+ # The name is determined by the description of the example.
165
+ mock_name = example.description.gsub(/[^\w\-\/]+/, '_').downcase
166
+
167
+ filename = File.join('spec/recordings/', mock_path, "#{mock_name}.yml")
168
+
169
+ # If we've already recorded this spec load the recordings
170
+ Net::IMAP.recordings = File.exist?(filename) ? YAML.load_file(filename) : nil
171
+
172
+ example.run
173
+
174
+ # If we haven't yet recorded the spec and there were some recordings,
175
+ # write them to a file.
176
+ return if File.exist?(filename) or Net::IMAP.recordings.empty?
177
+ FileUtils.mkdir_p(File.dirname(filename))
178
+ File.open(filename, 'w') { |f| YAML.dump(Net::IMAP.recordings, f) }
179
+ end
180
+ end
181
+ end