tweetstream 1.1.4 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of tweetstream might be problematic. Click here for more details.

data/.rspec CHANGED
@@ -1,2 +1 @@
1
- --colour
2
- --format doc
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ matrix:
3
+ allow_failures:
4
+ - rvm: ruby-head
5
+ rvm:
6
+ - rbx-19mode
7
+ - 1.8.7
8
+ - 1.9.2
9
+ - 1.9.3
10
+ - ruby-head
data/CHANGELOG.md CHANGED
@@ -40,4 +40,4 @@ Version 1.0.0
40
40
 
41
41
  * Swappable JSON backend support
42
42
  * Switches to use EventMachine instead of Yajl for the HTTP Stream
43
- * Support reconnect and on_error
43
+ * Support reconnect and on_error
data/Gemfile CHANGED
@@ -1,2 +1,3 @@
1
- source :rubygems
2
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
- # TweetStream
2
-
1
+ # TweetStream [![Build Status](https://secure.travis-ci.org/intridea/tweetstream.png?branch=master)][travis] [![Dependency Status](https://gemnasium.com/intridea/tweetstream.png?travis)][gemnasium]
3
2
  TweetStream provides simple Ruby access to [Twitter's Streaming API](https://dev.twitter.com/docs/streaming-api).
4
3
 
4
+ [travis]: http://travis-ci.org/intridea/tweetstream
5
+ [gemnasium]: https://gemnasium.com/intridea/tweetstream
6
+
5
7
  ## Installation
6
8
 
7
9
  gem install tweetstream
@@ -11,7 +13,6 @@ TweetStream provides simple Ruby access to [Twitter's Streaming API](https://dev
11
13
  Using TweetStream is quite simple:
12
14
 
13
15
  ```ruby
14
- require 'rubygems'
15
16
  require 'tweetstream'
16
17
 
17
18
  TweetStream.configure do |config|
@@ -129,7 +130,8 @@ TweetStream.configure do |config|
129
130
  end
130
131
  ```
131
132
 
132
- Available options are `:yajl`, `:json_gem`, `:json_pure`, and `:ok_json`.
133
+ Available options are `:oj`, `:yajl`, `:json_gem`, `:json_pure`, and
134
+ `:ok_json`.
133
135
 
134
136
  ## Handling Deletes and Rate Limitations
135
137
 
@@ -250,11 +252,6 @@ you will see a list of daemonization commands such as start, stop, and run.
250
252
 
251
253
  * SiteStream support
252
254
 
253
- ## <a name="dependencies"></a>Dependency Status
254
- [![Dependency Status](https://gemnasium.com/intridea/tweetstream.png?travis)][gemnasium]
255
-
256
- [gemnasium]: https://gemnasium.com/intridea/tweetstream
257
-
258
255
  ## Contributing
259
256
 
260
257
  * Fork the project.
data/examples/oauth.rb CHANGED
@@ -18,4 +18,4 @@ end
18
18
 
19
19
  client.track("yankees") do |status|
20
20
  puts "#{status.text}"
21
- end
21
+ end
@@ -108,9 +108,9 @@ module TweetStream
108
108
  # the first pair denoting the southwest corner of the box
109
109
  # longitude/latitude pairs, separated by commas. The first pair specifies the southwest corner of the box.
110
110
  def locations(*locations_map, &block)
111
- query_params = locations_map.pop if locations_map.last.is_a?(::Hash)
112
- query_params ||= {}
113
- filter(query_params.merge(:locations => locations_map), &block)
111
+ query_params = locations_map.pop if locations_map.last.is_a?(::Hash)
112
+ query_params ||= {}
113
+ filter(query_params.merge(:locations => locations_map), &block)
114
114
  end
115
115
 
116
116
  # Make a call to the statuses/filter method of the Streaming API,
@@ -148,6 +148,27 @@ module TweetStream
148
148
  end
149
149
  end
150
150
 
151
+ # Set a Proc to be run when a scrub_geo notice is received
152
+ # from the Twitter stream. For example:
153
+ #
154
+ # @client = TweetStream::Client.new
155
+ # @client.on_scrub_geo do |up_to_status_id, user_id|
156
+ # Tweet.where(:status_id <= up_to_status_id)
157
+ # end
158
+ #
159
+ # Block must take two arguments: the upper status id and the user id.
160
+ # If no block is given, it will return the currently set
161
+ # scrub_geo proc. When a block is given, the TweetStream::Client
162
+ # object is returned to allow for chaining.
163
+ def on_scrub_geo(&block)
164
+ if block_given?
165
+ @on_scrub_geo = block
166
+ self
167
+ else
168
+ @on_scrub_geo
169
+ end
170
+ end
171
+
151
172
  # Set a Proc to be run when a rate limit notice is received
152
173
  # from the Twitter stream. For example:
153
174
  #
@@ -323,6 +344,7 @@ module TweetStream
323
344
  def connect(path, query_parameters = {}, &block)
324
345
  method = query_parameters.delete(:method) || :get
325
346
  delete_proc = query_parameters.delete(:delete) || self.on_delete
347
+ scrub_geo_proc = query_parameters.delete(:scrub_geo) || self.on_scrub_geo
326
348
  limit_proc = query_parameters.delete(:limit) || self.on_limit
327
349
  error_proc = query_parameters.delete(:error) || self.on_error
328
350
  reconnect_proc = query_parameters.delete(:reconnect) || self.on_reconnect
@@ -338,13 +360,13 @@ module TweetStream
338
360
  uri = method == :get ? build_uri(path, params) : build_uri(path)
339
361
 
340
362
  stream_params = {
341
- :path => uri,
342
- :method => method.to_s.upcase,
343
- :user_agent => user_agent,
344
- :on_inited => inited_proc,
345
- :filters => params.delete(:track),
346
- :params => params,
347
- :ssl => true
363
+ :path => uri,
364
+ :method => method.to_s.upcase,
365
+ :user_agent => user_agent,
366
+ :on_inited => inited_proc,
367
+ :filters => params.delete(:track),
368
+ :params => params,
369
+ :ssl => true
348
370
  }.merge(auth_params).merge(extra_stream_parameters)
349
371
 
350
372
  if @on_interval_proc.is_a?(Proc)
@@ -373,6 +395,8 @@ module TweetStream
373
395
  hash = TweetStream::Hash.new(raw_hash)
374
396
  if hash[:delete] && hash[:delete][:status]
375
397
  delete_proc.call(hash[:delete][:status][:id], hash[:delete][:status][:user_id]) if delete_proc.is_a?(Proc)
398
+ elsif hash[:scrub_geo] && hash[:scrub_geo][:up_to_status_id]
399
+ scrub_geo_proc.call(hash[:scrub_geo][:up_to_status_id], hash[:scrub_geo][:user_id]) if scrub_geo_proc.is_a?(Proc)
376
400
  elsif hash[:limit] && hash[:limit][:track]
377
401
  limit_proc.call(hash[:limit][:track]) if limit_proc.is_a?(Proc)
378
402
 
@@ -431,7 +455,7 @@ module TweetStream
431
455
  protected
432
456
 
433
457
  def parser_from(parser)
434
- MultiJson.engine = parser
458
+ MultiJson.adapter = parser
435
459
  MultiJson
436
460
  end
437
461
 
@@ -455,7 +479,7 @@ module TweetStream
455
479
  def normalize_filter_parameters(query_parameters = {})
456
480
  [:follow, :track, :locations].each do |param|
457
481
  if query_parameters[param].kind_of?(Array)
458
- query_parameters[param] = query_parameters[param].flatten.collect{|q| q.to_s}.join(',')
482
+ query_parameters[param] = query_parameters[param].flatten.collect { |q| q.to_s }.join(',')
459
483
  elsif query_parameters[param]
460
484
  query_parameters[param] = query_parameters[param].to_s
461
485
  end
@@ -465,25 +489,25 @@ module TweetStream
465
489
 
466
490
  def auth_params
467
491
  case auth_method
468
- when :basic
469
- return :auth => "#{username}:#{password}"
470
- when :oauth
471
- return :oauth => {
472
- :consumer_key => consumer_key,
473
- :consumer_secret => consumer_secret,
474
- :access_key => oauth_token,
475
- :access_secret => oauth_token_secret
476
- }
492
+ when :basic
493
+ return :auth => "#{username}:#{password}"
494
+ when :oauth
495
+ return :oauth => {
496
+ :consumer_key => consumer_key,
497
+ :consumer_secret => consumer_secret,
498
+ :access_key => oauth_token,
499
+ :access_secret => oauth_token_secret
500
+ }
477
501
  end
478
502
  end
479
503
 
480
504
  def yield_message_to(procedure, message)
481
505
  if procedure.is_a?(Proc)
482
506
  case procedure.arity
483
- when 1
484
- procedure.call(message)
485
- when 2
486
- procedure.call(message, self)
507
+ when 1
508
+ procedure.call(message)
509
+ when 2
510
+ procedure.call(message, self)
487
511
  end
488
512
  end
489
513
  end
@@ -17,7 +17,7 @@ module TweetStream
17
17
  :oauth_token_secret].freeze
18
18
 
19
19
  # The parser that will be used to connect if none is set
20
- DEFAULT_PARSER = MultiJson.default_engine
20
+ DEFAULT_PARSER = MultiJson.default_adapter
21
21
 
22
22
  # By default, don't set a username
23
23
  DEFAULT_USERNAME = nil
@@ -12,4 +12,4 @@ module TweetStream
12
12
  super("Failed to reconnect after #{retries} tries.")
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -6,7 +6,17 @@ class TweetStream::Hash < ::Hash #:nodoc: all
6
6
  self[key.to_sym] = value
7
7
  end
8
8
  end
9
-
9
+
10
+ # This shim is necessary since method_missing won't be invoked for #id on
11
+ # Ruby < 1.9
12
+ def id
13
+ if key?(:id)
14
+ self[:id]
15
+ else
16
+ super
17
+ end
18
+ end
19
+
10
20
  def method_missing(method_name, *args)
11
21
  if key?(method_name.to_sym)
12
22
  self[method_name.to_sym]
@@ -14,4 +24,4 @@ class TweetStream::Hash < ::Hash #:nodoc: all
14
24
  super
15
25
  end
16
26
  end
17
- end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module TweetStream
2
- VERSION = '1.1.4'
2
+ VERSION = '1.1.5'
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,11 @@
1
- require 'simplecov'
1
+ unless ENV['CI']
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_group 'Tweetstream', 'lib/tweetstream'
5
+ add_group 'Specs', 'spec'
6
+ end
7
+ end
8
+
2
9
  require 'tweetstream'
3
10
  require 'rspec'
4
11
  require 'yajl'
@@ -107,7 +107,16 @@ describe TweetStream::Client do
107
107
  end
108
108
  end
109
109
 
110
- it 'should call the on_delete if specified' do
110
+ it 'should call the on_scrub_geo if specified' do
111
+ scrub_geo = '{ "scrub_geo": { "user_id": 1234, "user_id_str": "1234", "up_to_status_id":9876, "up_to_status_id_string": "9876" } }'
112
+ @stream.should_receive(:each_item).and_yield(scrub_geo)
113
+ @client.on_scrub_geo do |up_to_status_id, user_id|
114
+ up_to_status_id.should == 9876
115
+ user_id.should == 1234
116
+ end.track('abc')
117
+ end
118
+
119
+ it 'should call the delete if specified' do
111
120
  delete = '{ "delete": { "status": { "id": 1234, "user_id": 3 } } }'
112
121
  @stream.should_receive(:each_item).and_yield(delete)
113
122
  @client.on_delete do |id, user_id|
@@ -16,4 +16,4 @@ describe TweetStream::Hash do
16
16
  it 'should still throw NoMethod for non-existent keys' do
17
17
  lambda{TweetStream::Hash.new({}).akabi}.should raise_error(NoMethodError)
18
18
  end
19
- end
19
+ end
@@ -6,7 +6,7 @@ describe 'TweetStream MultiJson Support' do
6
6
  end
7
7
 
8
8
  it "should default to the MultiJson default's paser" do
9
- TweetStream::Client.new.json_parser.engine.to_s.should == MultiJson.engine.to_s
9
+ TweetStream::Client.new.json_parser.engine.to_s.should == MultiJson.adapter.to_s
10
10
  end
11
11
 
12
12
  [:json_gem, :yajl, :json_pure].each do |engine|
@@ -18,7 +18,7 @@ describe 'TweetStream MultiJson Support' do
18
18
  config.parser = engine
19
19
  end
20
20
  @client = TweetStream::Client.new
21
- @class_name = "MultiJson::Engines::#{engine.to_s.split('_').map{|s| s.capitalize}.join('')}"
21
+ @class_name = "MultiJson::Adapters::#{engine.to_s.split('_').map{|s| s.capitalize}.join('')}"
22
22
  end
23
23
 
24
24
  it 'should set the parser to the appropriate class' do
@@ -39,4 +39,4 @@ describe 'TweetStream MultiJson Support' do
39
39
  @client.parser = FakeParser
40
40
  @client.json_parser.engine.should == FakeParser
41
41
  end
42
- end
42
+ end
data/tweetstream.gemspec CHANGED
@@ -11,18 +11,18 @@ Gem::Specification.new do |s|
11
11
  s.summary = %q{TweetStream is a simple wrapper for consuming the Twitter Streaming API.}
12
12
  s.homepage = 'http://github.com/intridea/tweetstream'
13
13
 
14
- s.add_dependency 'twitter-stream', '= 0.1.14'
14
+ s.add_dependency 'twitter-stream', ['>= 0.1.14', '< 1']
15
15
  s.add_dependency 'daemons', '~> 1.1'
16
- s.add_dependency 'multi_json', '>= 1.0'
16
+ s.add_dependency 'multi_json', '~> 1.3'
17
17
 
18
- s.add_development_dependency 'rake', '~> 0.9'
19
- s.add_development_dependency 'simplecov', '~> 0.5.4'
20
- s.add_development_dependency 'yard', '~> 0.7'
21
- s.add_development_dependency 'rdiscount', '~> 1.6'
22
- s.add_development_dependency 'rspec', '~> 2.7'
23
- s.add_development_dependency 'yajl-ruby', '~> 1.0'
24
- s.add_development_dependency 'json', '~> 1.6'
25
- s.add_development_dependency 'guard-rspec', '~> 0.5'
18
+ s.add_development_dependency 'rake'
19
+ s.add_development_dependency 'simplecov'
20
+ s.add_development_dependency 'yard'
21
+ s.add_development_dependency 'rdiscount'
22
+ s.add_development_dependency 'rspec'
23
+ s.add_development_dependency 'yajl-ruby'
24
+ s.add_development_dependency 'json'
25
+ s.add_development_dependency 'guard-rspec'
26
26
 
27
27
  s.files = `git ls-files`.split("\n")
28
28
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tweetstream
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,22 +10,33 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-02-28 00:00:00.000000000Z
13
+ date: 2012-04-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: twitter-stream
17
- requirement: &2152607200 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
- - - =
20
+ - - ! '>='
21
21
  - !ruby/object:Gem::Version
22
22
  version: 0.1.14
23
+ - - <
24
+ - !ruby/object:Gem::Version
25
+ version: '1'
23
26
  type: :runtime
24
27
  prerelease: false
25
- version_requirements: *2152607200
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.14
34
+ - - <
35
+ - !ruby/object:Gem::Version
36
+ version: '1'
26
37
  - !ruby/object:Gem::Dependency
27
38
  name: daemons
28
- requirement: &2152606040 !ruby/object:Gem::Requirement
39
+ requirement: !ruby/object:Gem::Requirement
29
40
  none: false
30
41
  requirements:
31
42
  - - ~>
@@ -33,106 +44,156 @@ dependencies:
33
44
  version: '1.1'
34
45
  type: :runtime
35
46
  prerelease: false
36
- version_requirements: *2152606040
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: '1.1'
37
53
  - !ruby/object:Gem::Dependency
38
54
  name: multi_json
39
- requirement: &2152605380 !ruby/object:Gem::Requirement
55
+ requirement: !ruby/object:Gem::Requirement
40
56
  none: false
41
57
  requirements:
42
- - - ! '>='
58
+ - - ~>
43
59
  - !ruby/object:Gem::Version
44
- version: '1.0'
60
+ version: '1.3'
45
61
  type: :runtime
46
62
  prerelease: false
47
- version_requirements: *2152605380
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
48
69
  - !ruby/object:Gem::Dependency
49
70
  name: rake
50
- requirement: &2152604700 !ruby/object:Gem::Requirement
71
+ requirement: !ruby/object:Gem::Requirement
51
72
  none: false
52
73
  requirements:
53
- - - ~>
74
+ - - ! '>='
54
75
  - !ruby/object:Gem::Version
55
- version: '0.9'
76
+ version: '0'
56
77
  type: :development
57
78
  prerelease: false
58
- version_requirements: *2152604700
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
59
85
  - !ruby/object:Gem::Dependency
60
86
  name: simplecov
61
- requirement: &2152603860 !ruby/object:Gem::Requirement
87
+ requirement: !ruby/object:Gem::Requirement
62
88
  none: false
63
89
  requirements:
64
- - - ~>
90
+ - - ! '>='
65
91
  - !ruby/object:Gem::Version
66
- version: 0.5.4
92
+ version: '0'
67
93
  type: :development
68
94
  prerelease: false
69
- version_requirements: *2152603860
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
70
101
  - !ruby/object:Gem::Dependency
71
102
  name: yard
72
- requirement: &2152603300 !ruby/object:Gem::Requirement
103
+ requirement: !ruby/object:Gem::Requirement
73
104
  none: false
74
105
  requirements:
75
- - - ~>
106
+ - - ! '>='
76
107
  - !ruby/object:Gem::Version
77
- version: '0.7'
108
+ version: '0'
78
109
  type: :development
79
110
  prerelease: false
80
- version_requirements: *2152603300
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
81
117
  - !ruby/object:Gem::Dependency
82
118
  name: rdiscount
83
- requirement: &2152602660 !ruby/object:Gem::Requirement
119
+ requirement: !ruby/object:Gem::Requirement
84
120
  none: false
85
121
  requirements:
86
- - - ~>
122
+ - - ! '>='
87
123
  - !ruby/object:Gem::Version
88
- version: '1.6'
124
+ version: '0'
89
125
  type: :development
90
126
  prerelease: false
91
- version_requirements: *2152602660
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
92
133
  - !ruby/object:Gem::Dependency
93
134
  name: rspec
94
- requirement: &2152601880 !ruby/object:Gem::Requirement
135
+ requirement: !ruby/object:Gem::Requirement
95
136
  none: false
96
137
  requirements:
97
- - - ~>
138
+ - - ! '>='
98
139
  - !ruby/object:Gem::Version
99
- version: '2.7'
140
+ version: '0'
100
141
  type: :development
101
142
  prerelease: false
102
- version_requirements: *2152601880
143
+ version_requirements: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
103
149
  - !ruby/object:Gem::Dependency
104
150
  name: yajl-ruby
105
- requirement: &2152601160 !ruby/object:Gem::Requirement
151
+ requirement: !ruby/object:Gem::Requirement
106
152
  none: false
107
153
  requirements:
108
- - - ~>
154
+ - - ! '>='
109
155
  - !ruby/object:Gem::Version
110
- version: '1.0'
156
+ version: '0'
111
157
  type: :development
112
158
  prerelease: false
113
- version_requirements: *2152601160
159
+ version_requirements: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
114
165
  - !ruby/object:Gem::Dependency
115
166
  name: json
116
- requirement: &2152600600 !ruby/object:Gem::Requirement
167
+ requirement: !ruby/object:Gem::Requirement
117
168
  none: false
118
169
  requirements:
119
- - - ~>
170
+ - - ! '>='
120
171
  - !ruby/object:Gem::Version
121
- version: '1.6'
172
+ version: '0'
122
173
  type: :development
123
174
  prerelease: false
124
- version_requirements: *2152600600
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ none: false
177
+ requirements:
178
+ - - ! '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
125
181
  - !ruby/object:Gem::Dependency
126
182
  name: guard-rspec
127
- requirement: &2152599980 !ruby/object:Gem::Requirement
183
+ requirement: !ruby/object:Gem::Requirement
128
184
  none: false
129
185
  requirements:
130
- - - ~>
186
+ - - ! '>='
131
187
  - !ruby/object:Gem::Version
132
- version: '0.5'
188
+ version: '0'
133
189
  type: :development
134
190
  prerelease: false
135
- version_requirements: *2152599980
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ none: false
193
+ requirements:
194
+ - - ! '>='
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
136
197
  description: TweetStream allows you to easily consume the Twitter Streaming API utilizing
137
198
  the YAJL Ruby gem.
138
199
  email:
@@ -146,7 +207,7 @@ files:
146
207
  - .gemtest
147
208
  - .gitignore
148
209
  - .rspec
149
- - .simplecov
210
+ - .travis.yml
150
211
  - .yardopts
151
212
  - CHANGELOG.md
152
213
  - Gemfile
@@ -198,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
259
  version: '0'
199
260
  requirements: []
200
261
  rubyforge_project:
201
- rubygems_version: 1.8.10
262
+ rubygems_version: 1.8.24
202
263
  signing_key:
203
264
  specification_version: 3
204
265
  summary: TweetStream is a simple wrapper for consuming the Twitter Streaming API.
data/.simplecov DELETED
@@ -1,4 +0,0 @@
1
- SimpleCov.start do
2
- add_group 'Tweetstream', 'lib/tweetstream'
3
- add_group 'Specs', 'spec'
4
- end