mac-say 0.1.0 → 0.2.2

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/lib/mac/say.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'say/version'
3
+ require_relative 'say/voices_attributes'
4
+
3
5
  require 'English'
4
6
 
5
7
  # Wrapper namespace module for a Say class
@@ -8,7 +10,7 @@ module Mac
8
10
  # Allows to use simple TTS on Mac right from Ruby scripts
9
11
  class Say
10
12
  # A regex pattern to parse say voices list output
11
- VOICES_PATTERN = /(^[\w-]+)\s+([\w-]+)\s+#\s([\p{Graph}\p{Zs}]+$)/i
13
+ VOICE_PATTERN = /(^[\w\s-]+)\s+([\w-]+)\s+#\s([\p{Graph}\p{Zs}]+$)/i
12
14
 
13
15
  # An error raised when `say` command couldn't be found
14
16
  class CommandNotFound < StandardError; end
@@ -19,8 +21,20 @@ module Mac
19
21
  # An error raised when the given voice isn't valid
20
22
  class VoiceNotFound < StandardError; end
21
23
 
22
- # An error raised when there is no a feature of voice to match
23
- class UnknownVoiceFeature < StandardError; end
24
+ # An error raised when there is no a attribute of voice to match
25
+ class UnknownVoiceAttribute < StandardError; end
26
+
27
+ # The list of the voice attributes available
28
+ VOICE_ATTRIBUTES = [
29
+ :name,
30
+ :language,
31
+ :country,
32
+ :sample,
33
+ :gender,
34
+ :joke,
35
+ :quality,
36
+ :singing
37
+ ]
24
38
 
25
39
  # Current voices list
26
40
  #
@@ -29,20 +43,24 @@ module Mac
29
43
  # Mac::Say.voices #=>
30
44
  # [
31
45
  # {
32
- # :name => :agnes,
33
- # :iso_code => {
34
- # :language => :en,
35
- # :country => :us
36
- # },
37
- # :sample => "Isn't it nice to have a computer that will talk to you?"
46
+ # :name => :agnes,
47
+ # :language => :en,
48
+ # :country => :us,
49
+ # :sample => "Isn't it nice to have a computer that will talk to you?",
50
+ # :gender => :female,
51
+ # :joke => false,
52
+ # :quality => :low,
53
+ # :singing => false
38
54
  # },
39
55
  # {
40
- # :name => :albert,
41
- # :iso_code => {
42
- # :language => :en,
43
- # :country => :us
44
- # },
45
- # :sample => " I have a frog in my throat. No, I mean a real frog!"
56
+ # :name => :albert,
57
+ # :language => :en,
58
+ # :country => :us,
59
+ # :sample => "I have a frog in my throat. No, I mean a real frog!",
60
+ # :gender => :male,
61
+ # :joke => true,
62
+ # :quality => :medium,
63
+ # :singing => false
46
64
  # },
47
65
  # ...
48
66
  # ]
@@ -94,7 +112,7 @@ module Mac
94
112
  # Providing file, voice or rate arguments changes instance state and influence
95
113
  # all the subsequent #say calls unless they have their own custom arguments
96
114
  #
97
- # @param string [String] a text to read using say command
115
+ # @param string [String] a text to read using say command (default: nil)
98
116
  # @param file [String] path to the file to read (default: nil)
99
117
  # @param voice [Symbol] voice to be used by the say command (default: :alex)
100
118
  # @param rate [Integer] speech rate in words per minute (default: 175) accepts values in (175..720)
@@ -126,31 +144,67 @@ module Mac
126
144
  execute_command(string)
127
145
  end
128
146
 
129
- # Find a voice by one of its features (e.g. :name, :language, :country)
147
+ # Look for voices by their attributes (e.g. :name, :language, :country, :gender, etc.)
148
+ #
149
+ # @overload voice(attribute, value)
150
+ # @param attribute [Symbol] the attribute to search voices by
151
+ # @param value [Symbol, String] the value of the attribute to search voices by
152
+ # @overload voice(&block)
153
+ # @yield [voice] Passes the given block to @voices.find_all
154
+ #
155
+ # @return [Array<Hash>, Hash, nil] an array with all the voices matched by the attribute or
156
+ # a voice Hash if only one voice corresponds to the attribute, nil if no voices found
157
+ #
158
+ # @example Find voices by one or more attributes
159
+ #
160
+ # Mac::Say.new.voice(:joke, false)
161
+ # Mac::Say.new.voice(:gender, :female)
130
162
  #
131
- # @return [Array<Hash>, Hash] an array with all the voices matched by the feature or
132
- # a voice Hash if only one voice corresponds to the feature
163
+ # Mac::Say.new.voice { |v| v[:joke] == true && v[:gender] == :female }
164
+ # Mac::Say.new.voice { |v| v[:language] == :en && v[:gender] == :male && v[:quality] == :high && v[:joke] == false }
133
165
  #
134
- # @raise [UnknownVoiceFeature] if the voice feature isn't supported
135
- def self.voice(feature, name)
166
+ # @raise [UnknownVoiceAttribute] if the voice attribute isn't supported def self.voice(attribute = nil, value = nil, &block)
167
+ def self.voice(attribute = nil, value = nil, &block)
136
168
  mac = new
137
- mac.voice(feature, name)
169
+
170
+ if block_given?
171
+ mac.voice(&block)
172
+ else
173
+ mac.voice(attribute, value)
174
+ end
138
175
  end
139
176
 
140
- # Find a voice by one of its features (e.g. :name, :language, :country)
177
+ # Look for voices by their attributes (e.g. :name, :language, :country, :gender, etc.)
141
178
  #
142
- # @return [Array<Hash>, Hash] an array with all the voices matched by the feature or
143
- # a voice Hash if only one voice corresponds to the feature
179
+ # @overload voice(attribute, value)
180
+ # @param attribute [Symbol] the attribute to search voices by
181
+ # @param value [Symbol, String] the value of the attribute to search voices by
182
+ # @overload voice(&block)
183
+ # @yield [voice] Passes the given block to @voices.find_all
144
184
  #
145
- # @raise [UnknownVoiceFeature] if the voice feature isn't supported
146
- def voice(feature, value)
147
- raise UnknownVoiceFeature, "Voice has no '#{feature}' feature" unless [:name, :language, :country].include?(feature)
148
-
149
- condition = feature == :name ? ->(v) { v[feature] == value } : ->(v) { v[:iso_code][feature] == value }
150
- found_voices = @voices.find_all(&condition)
185
+ # @return [Array<Hash>, Hash, nil] an array with all the voices matched by the attribute or
186
+ # a voice Hash if only one voice corresponds to the attribute, nil if no voices found
187
+ #
188
+ # @example Find voices by one or more attributes
189
+ #
190
+ # Mac::Say.new.voice(:joke, false)
191
+ # Mac::Say.new.voice(:gender, :female)
192
+ #
193
+ # Mac::Say.new.voice { |v| v[:joke] == true && v[:gender] == :female }
194
+ # Mac::Say.new.voice { |v| v[:language] == :en && v[:gender] == :male && v[:quality] == :high && v[:joke] == false }
195
+ #
196
+ # @raise [UnknownVoiceAttribute] if the voice attribute isn't supported
197
+ def voice(attribute = nil, value = nil, &block)
198
+ return unless (attribute && !value.nil?) || block_given?
199
+ raise UnknownVoiceAttribute, "Voice has no '#{attribute}' attribute" if attribute && !VOICE_ATTRIBUTES.include?(attribute)
200
+
201
+ if block_given?
202
+ found_voices = @voices.find_all(&block)
203
+ else
204
+ found_voices = @voices.find_all {|voice| voice[attribute] === value }
205
+ end
151
206
 
152
207
  return if found_voices.empty?
153
-
154
208
  found_voices.count == 1 ? found_voices.first : found_voices
155
209
  end
156
210
 
@@ -161,20 +215,24 @@ module Mac
161
215
  # Mac::Say.voices #=>
162
216
  # [
163
217
  # {
164
- # :name => :agnes,
165
- # :iso_code => {
166
- # :language => :en,
167
- # :country => :us
168
- # },
169
- # :sample => "Isn't it nice to have a computer that will talk to you?"
218
+ # :name => :agnes,
219
+ # :language => :en,
220
+ # :country => :us,
221
+ # :sample => "Isn't it nice to have a computer that will talk to you?",
222
+ # :gender => :female,
223
+ # :joke => false,
224
+ # :quality => :low,
225
+ # :singing => false
170
226
  # },
171
227
  # {
172
- # :name => :albert,
173
- # :iso_code => {
174
- # :language => :en,
175
- # :country => :us
176
- # },
177
- # :sample => " I have a frog in my throat. No, I mean a real frog!"
228
+ # :name => :albert,
229
+ # :language => :en,
230
+ # :country => :us,
231
+ # :sample => "I have a frog in my throat. No, I mean a real frog!",
232
+ # :gender => :male,
233
+ # :joke => true,
234
+ # :quality => :medium,
235
+ # :singing => false
178
236
  # },
179
237
  # ...
180
238
  # ]
@@ -213,7 +271,7 @@ module Mac
213
271
  # @raise [FileNotFound] if the given file wasn't found or isn't readable by the current user
214
272
  def generate_command
215
273
  say_path = @config[:say_path]
216
- file = @config[:file]
274
+ file = @config[:file]
217
275
 
218
276
  raise CommandNotFound, "Command `say` couldn't be found by '#{@config[:say_path]}' path" unless valid_command_path? say_path
219
277
 
@@ -234,22 +292,30 @@ module Mac
234
292
  # @raise [CommandNotFound] if the say command wasn't found
235
293
  def load_voices
236
294
  return if @voices
295
+
237
296
  say_path = @config[:say_path]
238
297
  raise CommandNotFound, "Command `say` couldn't be found by '#{say_path}' path" unless valid_command_path? say_path
239
298
 
240
- @voices = `#{say_path} -v '?'`.scan(VOICES_PATTERN).map do |voice|
299
+ @voices = `#{say_path} -v '?'`.scan(VOICE_PATTERN).map do |voice|
241
300
  lang = voice[1].split(/[_-]/)
301
+ name = voice[0].strip.downcase.to_sym
302
+
303
+ additional_attributes = ADDITIONAL_VOICE_ATTRIBUTES[name] || ADDITIONAL_VOICE_ATTRIBUTES[:_unknown_voice]
304
+
242
305
  {
243
- name: voice[0].downcase.to_sym,
244
- iso_code: { language: lang[0].downcase.to_sym, country: lang[1].downcase.to_sym },
245
- sample: voice[2]
246
- }
306
+ name: name,
307
+ language: lang[0].downcase.to_sym,
308
+ country: lang[1].downcase.to_sym,
309
+ sample: voice[2].strip
310
+ }.merge(additional_attributes)
247
311
  end
248
312
  end
249
313
 
250
314
  # Checks voice existence by the name
251
315
  # Loads voices if they weren't loaded before
252
316
  #
317
+ # @param name [String, Symbol] the name of the voice to validate
318
+ #
253
319
  # @return [Boolean] if the voices name in the list of voices
254
320
  #
255
321
  # @raise [CommandNotFound] if the say command wasn't found
@@ -260,6 +326,8 @@ module Mac
260
326
 
261
327
  # Checks say command existence by the path
262
328
  #
329
+ # @param path [String] the path of the `say` command to validate
330
+ #
263
331
  # @return [Boolean] if the command exists and if it is executable
264
332
  def valid_command_path?(path)
265
333
  File.exist?(path) && File.executable?(path)
@@ -267,11 +335,11 @@ module Mac
267
335
 
268
336
  # Checks text file existence by the path
269
337
  #
338
+ # @param path [String] the path of the text file validate
339
+ #
270
340
  # @return [Boolean] if the file exists and if it is readable
271
341
  def valid_file_path?(path)
272
342
  path && File.exist?(path) && File.readable?(path)
273
343
  end
274
344
  end
275
345
  end
276
-
277
- p Mac::Say.voice(:name, 'fuck')
data/mac-say.gemspec CHANGED
@@ -30,16 +30,16 @@ Gem::Specification.new do |gem|
30
30
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
31
31
  gem.require_paths = ['lib']
32
32
 
33
- gem.add_development_dependency 'bundler', '~> 1.10'
34
- gem.add_development_dependency 'minitest', '~> 5.0'
35
- gem.add_development_dependency 'minitest-reporters', '~> 1.1'
36
- gem.add_development_dependency 'rake', '~> 12.0'
37
- gem.add_development_dependency 'simplecov', '~> 0.12'
33
+ gem.add_development_dependency 'bundler', '~> 2.3'
34
+ gem.add_development_dependency 'minitest', '~> 5.15'
35
+ gem.add_development_dependency 'minitest-reporters', '~> 1.5'
36
+ gem.add_development_dependency 'rake', '~> 13.0'
37
+ gem.add_development_dependency 'simplecov', '~> 0.16'
38
38
  gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
39
- gem.add_development_dependency 'yard', '~> 0.8.7.5'
40
- gem.add_development_dependency 'inch', '~> 0.7.1'
41
- gem.add_development_dependency 'redcarpet', '~> 3.4'
42
- gem.add_development_dependency 'github-markup', '~> 1.4'
43
- gem.add_development_dependency 'm', '~> 1.5'
44
- gem.add_development_dependency 'coveralls', '~> 0.8'
39
+ gem.add_development_dependency 'yard', '~> 0.9'
40
+ gem.add_development_dependency 'inch', '~> 0.8'
41
+ gem.add_development_dependency 'redcarpet', '~> 3.5'
42
+ gem.add_development_dependency 'github-markup', '~> 4.0'
43
+ gem.add_development_dependency 'm', '~> 1.6'
44
+ gem.add_development_dependency 'coveralls', '~> 0.8.23'
45
45
  end
data/test/fake/say CHANGED
@@ -9,6 +9,7 @@ Fiona en-scotland # Hello, my name is Fiona. I am a Scottish-Engli
9
9
  Serena en_GB # Hello, my name is Serena. I am a British-English voice.
10
10
  Ting-Ting zh_CN # 您好,我叫Ting-Ting。我讲中文普通话。
11
11
  Veena en_IN # Hello, my name is Veena. I am an Indian-English voice.
12
+ Test en_GB # Well I'm the most mysterious voice round here, believe me.
12
13
  VOICES
13
14
 
14
15
  VOICES_NAMES = [
@@ -16,7 +17,8 @@ VOICES_NAMES = [
16
17
  :daniel,
17
18
  :fiona,
18
19
  :'ting-ting',
19
- :veena
20
+ :veena,
21
+ :test
20
22
  ]
21
23
 
22
24
  options = {
data/test/helper.rb CHANGED
@@ -4,15 +4,19 @@ require 'rubygems'
4
4
  require 'coveralls'
5
5
  Coveralls.wear!
6
6
 
7
+ require 'simplecov'
8
+ SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new [
9
+ SimpleCov::Formatter::HTMLFormatter,
10
+ Coveralls::SimpleCov::Formatter
11
+ ]
12
+ SimpleCov.start
13
+
7
14
  begin
8
15
  require 'bundler/setup'
9
16
  rescue LoadError => error
10
17
  abort error.message
11
18
  end
12
19
 
13
- require 'simplecov'
14
- SimpleCov.start
15
-
16
20
  require 'minitest/autorun'
17
21
  require 'minitest/reporters'
18
22
 
data/test/test_mac-say.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'helper'
3
3
  require 'mac/say'
4
+ require 'mac/say/voices_attributes'
4
5
 
5
6
  describe 'Mac::Say as a macOS `say` wrapper' do
6
7
  describe 'On a class level' do
@@ -9,71 +10,72 @@ describe 'Mac::Say as a macOS `say` wrapper' do
9
10
  end
10
11
 
11
12
  it 'must have a VERSION constant' do
12
- Mac::Say.const_get('VERSION').wont_be_empty
13
+ _(Mac::Say.const_get('VERSION')).wont_be_empty
13
14
  end
14
15
 
15
16
  it 'must return available voices as an Array of Hashes' do
16
- Mac::Say.voices.wont_be_empty
17
- Mac::Say.voices.must_be_kind_of Array
17
+ _(Mac::Say.voices).wont_be_empty
18
+ _(Mac::Say.voices).must_be_kind_of Array
18
19
  end
19
20
 
20
21
  it 'must return specific Hash structure for a voice' do
21
22
  voice = Mac::Say.voice(:name, :alex)
22
- voice.wont_be_empty
23
- voice.must_be_kind_of Hash
24
- voice.keys.must_equal [:name, :iso_code, :sample]
23
+ _(voice).wont_be_empty
24
+ _(voice).must_be_kind_of Hash
25
+ _(voice.keys).must_equal Mac::Say::VOICE_ATTRIBUTES
25
26
  end
26
27
 
27
- it 'must return specific Hash structure for an iso_code' do
28
- voice = Mac::Say.voice(:name, :alex)
29
- voice.wont_be_empty
30
- voice[:iso_code].must_be_kind_of Hash
31
- voice[:iso_code].keys.must_equal [:language, :country]
32
- end
28
+ it 'must return additional attributes for known voices' do
29
+ voice_name = :alex
30
+ voice = Mac::Say.voice(:name, voice_name)
31
+ additional_voice_attributes = ADDITIONAL_VOICE_ATTRIBUTES[voice_name]
33
32
 
34
- it '.voice must search for a voice by name' do
35
- voice = Mac::Say.voice(:name, :alex)
36
- voice[:name].must_equal :alex
33
+ _(voice[:gender]).must_equal additional_voice_attributes[:gender]
34
+ _(voice[:joke]).must_equal additional_voice_attributes[:joke]
35
+ _(voice[:quality]).must_equal additional_voice_attributes[:quality]
37
36
  end
38
37
 
39
- it '.voice must search for a voice by country' do
40
- voice = Mac::Say.voice(:country, :scotland)
41
- voice[:name].must_equal :fiona
38
+ it '.voice must search for a voice using single attribute' do
39
+ voice = Mac::Say.voice(:name, :alex)
40
+ _(voice[:name]).must_equal :alex
42
41
  end
43
42
 
44
- it '.voice must search for a voice by language' do
45
- voices = Mac::Say.voice(:language, :en)
46
- voices.count.must_be :>, 2
43
+ it '.voice must search for a voice using block given' do
44
+ voices = Mac::Say.voice {|voice| voice[:language] == :en && voice[:joke] == false }
45
+ _(voices).must_be_kind_of Array
47
46
  end
48
47
 
49
48
  it '.voice must return one voice as a Hash' do
50
49
  voice = Mac::Say.voice(:name, :alex)
51
- voice.must_be_kind_of Hash
50
+ _(voice).must_be_kind_of Hash
52
51
  end
53
52
 
53
+ # For this test to pass you'd have to have more than
54
+ # one British voice on your machine!
54
55
  it '.voice must return an Array of voices if > 1' do
55
56
  voices = Mac::Say.voice(:country, :gb)
56
- voices.must_be_kind_of Array
57
+ _(voices).must_be_kind_of Array
57
58
  end
58
59
 
59
60
  it ".voice must return nil if voice wasn't found" do
60
61
  voices = Mac::Say.voice(:name, :xxx)
61
- voices.must_be_nil
62
+ _(voices).must_be_nil
62
63
  end
63
64
 
65
+
64
66
  it '.say must return 0 in successive speech' do
65
67
  expectation = ["#{@say_path} -v 'alex' -r 175", 0]
66
- Mac::Say.say('42').must_equal expectation
68
+ _(Mac::Say.say('42')).must_equal expectation
67
69
  end
68
70
 
69
71
  it '.say must use custom voice' do
70
72
  expectation = ["#{@say_path} -v 'alex' -r 175", 0]
71
- Mac::Say.say('42', :alex).must_equal expectation
73
+ _(Mac::Say.say('42', :alex)).must_equal expectation
72
74
  end
73
75
 
74
76
  it '.say must work with multiple lines' do
75
77
  expectation = ["#{@say_path} -v 'alex' -r 175", 0]
76
- Mac::Say.say(<<-TEXT, :alex).must_equal expectation
78
+ _(Mac::Say.say(<<-TEXT, :alex)).must_equal expectation
77
79
  1
78
80
  2
79
81
  3
@@ -81,15 +83,15 @@ describe 'Mac::Say as a macOS `say` wrapper' do
81
83
  end
82
84
 
83
85
  it '.say must fail on wrong voice' do
84
- -> {
86
+ _{
85
87
  Mac::Say.say 'OMG! I lost my voice!', :wrong
86
88
  }.must_raise Mac::Say::VoiceNotFound
87
89
  end
88
90
 
89
- it '.voice must fail on wrong voice feature' do
90
- -> {
91
+ it '.voice must fail on wrong voice attribute' do
92
+ _{
91
93
  Mac::Say.voice(:tone, :enthusiastic)
92
- }.must_raise Mac::Say::UnknownVoiceFeature
94
+ }.must_raise Mac::Say::UnknownVoiceAttribute
93
95
  end
94
96
  end
95
97
 
@@ -100,24 +102,24 @@ describe 'Mac::Say as a macOS `say` wrapper' do
100
102
  end
101
103
 
102
104
  it 'must instantiate Mac::Say' do
103
- @reader.must_be_instance_of Mac::Say
105
+ _(@reader).must_be_instance_of Mac::Say
104
106
  end
105
107
 
106
108
  it '#say must return 0 on successive speech' do
107
109
  expectation = ["#{@say_path} -v 'alex' -r 175", 0]
108
- @reader.say(string: '42').must_equal expectation
110
+ _(@reader.say(string: '42')).must_equal expectation
109
111
  end
110
112
 
111
113
  it '#read must be a synonym of #say' do
112
114
  expectation = ["#{@say_path} -v 'alex' -r 175", 0]
113
- @reader.read(string: '42').must_equal expectation
115
+ _(@reader.read(string: '42')).must_equal expectation
114
116
  end
115
117
 
116
118
  it '#say must support :file' do
117
119
  absolute_path = File.absolute_path './fixtures/text/en_gb_test.txt', File.dirname(__FILE__)
118
120
  expectation = ["#{@say_path} -f #{absolute_path} -v 'alex' -r 175", 0]
119
121
 
120
- @reader.say(file: absolute_path).must_equal expectation
122
+ _(@reader.say(file: absolute_path)).must_equal expectation
121
123
  end
122
124
 
123
125
  it '#say must read :file from initial config' do
@@ -125,7 +127,17 @@ describe 'Mac::Say as a macOS `say` wrapper' do
125
127
  expectation = ["#{@say_path} -f #{absolute_path} -v 'alex' -r 175", 0]
126
128
 
127
129
  @reader = Mac::Say.new(file: absolute_path)
128
- @reader.say.must_equal expectation
130
+ _(@reader.say).must_equal expectation
131
+ end
132
+
133
+ it 'must return nil additional attrs for unknown voices' do
134
+ if ENV['USE_FAKE_SAY']
135
+ voice = @reader.voice(:name, :test)
136
+
137
+ _(voice[:gender]).must_be_nil
138
+ _(voice[:joke]).must_be_nil
139
+ _(voice[:quality]).must_be_nil
140
+ end
129
141
  end
130
142
 
131
143
  it '#say must change :file from initial config' do
@@ -136,90 +148,90 @@ describe 'Mac::Say as a macOS `say` wrapper' do
136
148
 
137
149
  # init
138
150
  @reader = Mac::Say.new(file: gb_absolute_path)
139
- @reader.config[:file].must_equal gb_absolute_path
151
+ _(@reader.config[:file]).must_equal gb_absolute_path
140
152
 
141
153
  # change
142
- @reader.say(file: us_absolute_path).must_equal expectation
143
- @reader.config[:file].must_equal us_absolute_path
154
+ _(@reader.say(file: us_absolute_path)).must_equal expectation
155
+ _(@reader.config[:file]).must_equal us_absolute_path
144
156
  end
145
157
 
146
158
  it '#say must prioritise :file over :string' do
147
159
  absolute_path = File.absolute_path './fixtures/text/en_gb_test.txt', File.dirname(__FILE__)
148
160
  expectation = ["#{@say_path} -f #{absolute_path} -v 'alex' -r 175", 0]
149
161
 
150
- @reader.say(string: 'test', file: absolute_path).must_equal expectation
162
+ _(@reader.say(string: 'test', file: absolute_path)).must_equal expectation
151
163
  end
152
164
 
153
165
  it '#say must support custom :rate' do
154
166
  expectation = ["#{@say_path} -v 'alex' -r 250", 0]
155
- @reader.say(string: '42', rate: 250).must_equal expectation
167
+ _(@reader.say(string: '42', rate: 250)).must_equal expectation
156
168
  end
157
169
 
158
170
  it '#say must support custom :voice' do
159
171
  expectation = ["#{@say_path} -v 'fiona' -r 175", 0]
160
- @reader.say(string: '42', voice: :fiona).must_equal expectation
172
+ _(@reader.say(string: '42', voice: :fiona)).must_equal expectation
161
173
  end
162
174
 
163
175
  it '#say must change the :voice' do
164
176
  expectation = ["#{@say_path} -v 'fiona' -r 175", 0]
165
- @reader.config[:voice].must_equal :alex
177
+ _(@reader.config[:voice]).must_equal :alex
166
178
 
167
- @reader.say(string: '42', voice: :fiona).must_equal expectation
168
- @reader.config[:voice].must_equal :fiona
179
+ _(@reader.say(string: '42', voice: :fiona)).must_equal expectation
180
+ _(@reader.config[:voice]).must_equal :fiona
169
181
 
170
- @reader.say(string: '13').must_equal expectation
182
+ _(@reader.say(string: '13')).must_equal expectation
171
183
  end
172
184
 
173
185
  it '#say must change the :rate' do
174
186
  expectation = ["#{@say_path} -v 'alex' -r 300", 0]
175
- @reader.config[:rate].must_equal 175
187
+ _(@reader.config[:rate]).must_equal 175
176
188
 
177
- @reader.say(string: '42', rate: 300).must_equal expectation
178
- @reader.config[:rate].must_equal 300
189
+ _(@reader.say(string: '42', rate: 300)).must_equal expectation
190
+ _(@reader.config[:rate]).must_equal 300
179
191
 
180
- @reader.say(string: '13').must_equal expectation
192
+ _(@reader.say(string: '13')).must_equal expectation
181
193
  end
182
194
 
183
195
  it '#say must fail on wrong initial voice' do
184
- -> {
196
+ _{
185
197
  talker = Mac::Say.new(voice: :wrong)
186
198
  talker.say string: 'OMG! I lost my voice!'
187
199
  }.must_raise Mac::Say::VoiceNotFound
188
200
  end
189
201
 
190
202
  it '#say must fail on wrong dynamic voice' do
191
- -> {
203
+ _{
192
204
  talker = Mac::Say.new
193
205
  talker.say string: 'OMG! I lost my voice!', voice: :wrong
194
206
  }.must_raise Mac::Say::VoiceNotFound
195
207
  end
196
208
 
197
209
  it '#voice must fail on wrong say path' do
198
- -> {
210
+ _{
199
211
  Mac::Say.new(say_path: '/wrong/wrong/path').voice(:name, :alex)
200
212
  }.must_raise Mac::Say::CommandNotFound
201
213
  end
202
214
 
203
215
  it '#say must fail on wrong say path' do
204
- -> {
216
+ _{
205
217
  Mac::Say.new(say_path: '/wrong/wrong/path').say 'test'
206
218
  }.must_raise Mac::Say::CommandNotFound
207
219
  end
208
220
 
209
221
  it '#say must fail on wrong file path' do
210
- -> {
222
+ _{
211
223
  Mac::Say.new.say(file: '/wrong/wrong/path')
212
224
  }.must_raise Mac::Say::FileNotFound
213
225
  end
214
226
 
215
- it '#voice must fail on wrong feature' do
216
- -> {
227
+ it '#voice must fail on wrong attribute' do
228
+ _{
217
229
  Mac::Say.new.voice(:articulation, :nostalgic)
218
- }.must_raise Mac::Say::UnknownVoiceFeature
230
+ }.must_raise Mac::Say::UnknownVoiceAttribute
219
231
  end
220
232
 
221
233
  it '#say must fail on initial wrong file path' do
222
- -> {
234
+ _{
223
235
  Mac::Say.new(file: '/wrong/wrong/path').say
224
236
  }.must_raise Mac::Say::FileNotFound
225
237
  end