xploy 0.1.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'xploy/api/http'
3
+ require 'xploy/error'
4
+
5
+ describe Xploy::Api::Http do
6
+ let('headers') { {'X-App' => 'appway'} }
7
+ let('request') do
8
+ double('Xploy::Api::Request').tap do |mock|
9
+ mock.stub('method_name').and_return('get')
10
+ mock.stub('path').and_return('/bar')
11
+ mock.stub('http_options').and_return(headers: headers)
12
+ end
13
+ end
14
+
15
+ it 'wraps errors' do
16
+ HTTParty.stub('get') { raise StandardError, 'foo' }
17
+ expect { subject.request 'http://foo', request }.to\
18
+ raise_error(Xploy::ConnectionError)
19
+ end
20
+
21
+ describe 'calls HTTParty' do
22
+ it do
23
+ HTTParty.should_receive('get').with('http://foo/bar',
24
+ headers: headers)
25
+ subject.request 'http://foo', request
26
+ end
27
+
28
+ it 'includes debug flag' do
29
+ HTTParty.should_receive('get').with('http://foo/bar',
30
+ headers: headers,
31
+ debug_output: STDOUT)
32
+ subject.request 'http://foo', request, true
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'xploy/error'
3
+ require 'xploy/api/request/body'
4
+
5
+ describe Xploy::Api::Request::Body do
6
+ context 'unknown file type' do
7
+ let('path') { 'foo/bar' }
8
+ before do
9
+ File.stub('exists?').with(path).and_return(true)
10
+ end
11
+ subject { described_class.new path }
12
+
13
+ its('path') { should eq path }
14
+ it { expect{subject.mime_type}.to raise_error(Xploy::ManifestFileTypeUnsupported) }
15
+ it { expect{subject.read}.to raise_error(Xploy::ManifestFileTypeUnsupported) }
16
+ end
17
+
18
+ context 'known file type but not existent' do
19
+ let('path') { 'foo/bar.json' }
20
+ before do
21
+ File.stub('exists?').with(path).and_return(false)
22
+ end
23
+ subject { described_class.new path }
24
+
25
+ its('path') { should eq path }
26
+ its('mime_type') { should eq 'application/json' }
27
+ it { expect{subject.read}.to raise_error(Xploy::ManifestFileNotFound) }
28
+ end
29
+
30
+ context 'correct file' do
31
+ let('path') { 'foo/bar.json' }
32
+ before do
33
+ File.stub('exists?').with(path).and_return(true)
34
+ File.stub('read').with(path).and_return('example data')
35
+ end
36
+ subject { described_class.new path }
37
+
38
+ its('path') { should eq path }
39
+ its('mime_type') { should eq 'application/json' }
40
+ its('read') { should eq 'example data' }
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'xploy/api/request'
3
+
4
+ describe Xploy::Api::Request do
5
+ subject { described_class.new 'get', '/foo' }
6
+
7
+ its('method_name') { should eq 'get' }
8
+ its('path') { should eq '/foo' }
9
+ its('headers') { should eq('X-App' => 'appway') }
10
+ its('body') { should eq nil }
11
+
12
+ context 'with manifest' do
13
+ let('manifest') { File.join(ASSETS_PATH, 'appway-example.json') }
14
+ subject { described_class.new 'get', '/foo', manifest: manifest }
15
+
16
+ its('method_name') { should eq 'get' }
17
+ its('path') { should eq '/foo' }
18
+ its('headers') { should eq('X-App' => 'appway',
19
+ 'Content-Type' => 'application/json') }
20
+ its('body.read') { should include('github.com/threez/appway-example') }
21
+ end
22
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+ require 'xploy/api'
3
+ require 'xploy/error'
4
+
5
+ describe Xploy::Api do
6
+ let!('parameter') do
7
+ parameter = double('Xploy::Parameter').tap do |p|
8
+ p.stub('[]').with(:servers).and_return(['http://foo',
9
+ 'http://bar'])
10
+ p.stub('[]').with(:debug).and_return(false)
11
+ p.stub('[]').with(:app).and_return(nil)
12
+ end
13
+ Xploy.stub('parameter').and_return(parameter)
14
+ end
15
+
16
+ subject('api') { described_class.new }
17
+
18
+ context 'mock HTTParty' do
19
+ before do
20
+ HTTParty.stub('get').and_return('list of apps')
21
+ end
22
+
23
+ subject { api.request 'list' }
24
+
25
+ it { should eq ['list of apps', 'list of apps'] }
26
+ end
27
+
28
+ context 'mock http + endpoints' do
29
+ let!('http') do
30
+ double('Http').tap do |mock|
31
+ mock.stub('request') do |server, request, debug|
32
+ Struct\
33
+ .new(:server, :http_options, :debug)\
34
+ .new(server, request.http_options, debug)
35
+ end
36
+ Xploy::Api::Http.stub('new').and_return(mock)
37
+ end
38
+ end
39
+ let!('endpoints') do
40
+ double('Endpoints').tap do |mock|
41
+ mock.stub('foo') do |options|
42
+ Xploy::Api::Request.new 'get', '/foo', options
43
+ end
44
+ Xploy::Api::Endpoints.stub('new').and_return(mock)
45
+ end
46
+ end
47
+
48
+ context 'apps' do
49
+ describe 'builds request for each server' do
50
+ subject { api.request 'foo' }
51
+
52
+ its('first.server') { should eq('http://foo') }
53
+ its('first.http_options') { should eq(:headers => {
54
+ "X-App" => "appway"}) }
55
+ its('first.debug') { should be_false }
56
+ its('last.server') { should eq('http://bar') }
57
+ its('last.http_options') { should eq(:headers => {
58
+ "X-App" => "appway"}) }
59
+ its('last.debug') { should be_false }
60
+ end
61
+ end
62
+
63
+ context 'app name + manifest' do
64
+ before do
65
+ manifest = File.join(ASSETS_PATH, 'appway-example.json')
66
+ File.stub('exists?').with(manifest).and_return(true)
67
+ File.stub('read').with(manifest).and_return('appway-example.json.data')
68
+ Xploy.parameter.stub('[]').with(:app).and_return(name: 'appway-example',
69
+ manifest: manifest)
70
+ end
71
+
72
+ describe 'builds request for each server' do
73
+ subject { api.request 'foo' }
74
+
75
+ its('first.server') { should eq('http://foo') }
76
+ its('first.http_options') { should eq(:headers => {
77
+ "X-App" => "appway",
78
+ "Content-Type" => "application/json"},
79
+ :body => "appway-example.json.data") }
80
+ its('first.debug') { should be_false }
81
+ its('last.server') { should eq('http://bar') }
82
+ its('last.http_options') { should eq(:headers => {
83
+ "X-App" => "appway",
84
+ "Content-Type" => "application/json"},
85
+ :body => "appway-example.json.data") }
86
+ its('last.debug') { should be_false }
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'xploy/cli'
3
+ require 'xploy/version'
4
+
5
+ describe Xploy::Cli do
6
+ let('parameter') do
7
+ double('Xploy::Parameter').tap do |mock|
8
+ mock.stub('rest').and_return([])
9
+ mock.stub('[]').with(:version).and_return(nil)
10
+ end
11
+ end
12
+ let('api') { double('api') }
13
+ let('out') { double('stdout').tap { |o| o.stub('puts') } }
14
+ before { Xploy.stub('parameter').and_return(parameter) }
15
+ subject { described_class.new api, out }
16
+
17
+ it 'prints usage per default' do
18
+ parameter.should_receive('print_help!')
19
+ subject.start
20
+ end
21
+
22
+ it 'prints version on :version parameter' do
23
+ parameter.stub('[]').with(:version).and_return(true)
24
+ out.should_receive('puts').with("xploy #{Xploy::VERSION}")
25
+ subject.start
26
+ end
27
+
28
+ it 'executes commands on api' do
29
+ api.should_receive('request').with('list').and_return('list result')
30
+ out.should_receive('puts').with('list result')
31
+ parameter.stub('rest').and_return(['list'])
32
+ subject.start
33
+ end
34
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require 'xploy/error'
3
+
4
+ describe Xploy::Error do
5
+ it { should be_kind_of(StandardError) }
6
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+ require 'xploy/parameter'
3
+
4
+ describe Xploy::Parameter do
5
+ its('global_config') { should eq(File.join(ENV['HOME'], '.xploy')) }
6
+ its('local_config') { should eq(File.join(Dir.pwd, '.xploy')) }
7
+
8
+ context 'mocked Configliere::Param' do
9
+ let('param') do
10
+ double('Configliere::Param').tap do |param|
11
+ param.stub('define')
12
+ param.stub('use')
13
+ param.stub('read')
14
+ param.stub('resolve!')
15
+ param.stub('[]')
16
+ param.stub('to_hash')
17
+ param.stub('rest')
18
+ end
19
+ end
20
+ before do
21
+ subject.stub('global_config').and_return('global-xploy-conf')
22
+ subject.stub('local_config').and_return('local-xploy-conf')
23
+ File.stub('exists?').with('global-xploy-conf').and_return(false)
24
+ File.stub('exists?').with('local-xploy-conf').and_return(false)
25
+ Configliere::Param.stub('new').and_return(param)
26
+ end
27
+
28
+ its('to_hash') { should eq({}) }
29
+ its('rest') { should eq([]) }
30
+ it { subject[:servers].should eq(nil)}
31
+
32
+ describe 'loads!' do
33
+ it 'reads global config when it exists' do
34
+ File.stub('exists?').with('global-xploy-conf').and_return(true)
35
+ param.should_receive('read').with('global-xploy-conf')
36
+ subject.reload!
37
+ end
38
+
39
+ it 'reads local config when it exists' do
40
+ File.stub('exists?').with('local-xploy-conf').and_return(true)
41
+ param.should_receive('read').with('local-xploy-conf')
42
+ subject.reload!
43
+ end
44
+
45
+ it 'uses commandline' do
46
+ param.should_receive('use').with(:commandline)
47
+ subject.reload!
48
+ end
49
+
50
+ it 'calls resolve' do
51
+ param.should_receive('resolve!')
52
+ subject.reload!
53
+ end
54
+
55
+ it 'defines servers' do
56
+ param.should_receive('define').with(:servers,
57
+ type: Array,
58
+ flag: 's',
59
+ description: 'all appway servers',
60
+ default: ['http://localhost:8000'])
61
+ subject.reload!
62
+ end
63
+
64
+ it 'defines app.name' do
65
+ param.should_receive('define').with('app.name',
66
+ flag: 'a',
67
+ description: 'name of your app')
68
+ subject.reload!
69
+ end
70
+
71
+ it 'defines app.manifest' do
72
+ param.should_receive('define').with('app.manifest',
73
+ flag: 'm',
74
+ description: 'path to your app.way file')
75
+ subject.reload!
76
+ end
77
+
78
+ it 'defines debug' do
79
+ param.should_receive('define').with(:debug,
80
+ flag: 'd',
81
+ description: 'print debug info to stdout',
82
+ default: false)
83
+ subject.reload!
84
+ end
85
+
86
+ it 'defines version' do
87
+ param.should_receive('define').with(:version,
88
+ flag: 'v',
89
+ description: 'print version info')
90
+ subject.reload!
91
+ end
92
+ end
93
+
94
+ context 'after reload!' do
95
+ before do
96
+ param.stub('[]').with(:servers).and_return('http://bar')
97
+ param.stub('to_hash').and_return(servers: ['http://bar'])
98
+ param.stub('rest').and_return([1,2,3])
99
+ subject.reload!
100
+ end
101
+ its('rest') { should eq([1,2,3]) }
102
+ its('to_hash') { should eq(servers: ['http://bar']) }
103
+ it { subject[:servers].should eq('http://bar') }
104
+ end
105
+
106
+ it 'print_help!' do
107
+ param.should_receive('[]=').with(:help, true)
108
+ param.should_receive('resolve!')
109
+ subject.print_help!
110
+ end
111
+ end
112
+
113
+ context 'custom XPLOY_CONFIG' do
114
+ let('xploy_config') { File.join(Dir.pwd, 'spec/assets/custom.xploy') }
115
+ before { ENV.stub('[]').and_return(xploy_config) }
116
+
117
+ its('global_config') { should eq(xploy_config) }
118
+ its('local_config') { should eq(File.join(Dir.pwd, '.xploy')) }
119
+ end
120
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require 'xploy'
3
+
4
+ describe Xploy do
5
+ its('parameter') { should be_kind_of(Xploy::Parameter) }
6
+ end
@@ -0,0 +1,28 @@
1
+ # this must before application code is required
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+
5
+ # This file was generated by the `rspec --init` command. Conventionally, all
6
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
7
+ # Require this file using `require "spec_helper"` to ensure that it is only
8
+ # loaded once.
9
+ #
10
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+
16
+ # Run specs in random order to surface order dependencies. If you find an
17
+ # order dependency and want to debug it, you can fix the order by providing
18
+ # the seed, which is printed after each run.
19
+ # --seed 1234
20
+ config.order = 'random'
21
+ end
22
+
23
+ # just add the lib folder to the load path
24
+ # -> actual 'require' calls will be done within each spec (speed + modularity)
25
+ lib = File.expand_path('../lib', File.dirname(__FILE__))
26
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
27
+
28
+ ASSETS_PATH = File.expand_path('assets', File.dirname(__FILE__))
data/xploy.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'xploy/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "xploy"
8
+ gem.version = Xploy::VERSION
9
+ gem.authors = ["Jens Bissinger"]
10
+ gem.email = ["mail@jens-bissinger.de"]
11
+ gem.description = %q{Appway client.}
12
+ gem.summary = %q{Provides the xploy command. See github.com/threez/appway.}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'configliere'
21
+ gem.add_dependency 'httparty'
22
+
23
+ gem.add_development_dependency 'rake'
24
+ gem.add_development_dependency 'rspec'
25
+ gem.add_development_dependency 'rspec-nc'
26
+ gem.add_development_dependency 'guard'
27
+ gem.add_development_dependency 'guard-rspec'
28
+ gem.add_development_dependency 'terminal-notifier-guard'
29
+ gem.add_development_dependency 'coveralls'
30
+ end
metadata ADDED
@@ -0,0 +1,242 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Jens Bissinger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: configliere
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: httparty
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec-nc
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: guard
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: guard-rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: terminal-notifier-guard
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: coveralls
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description: Appway client.
159
+ email:
160
+ - mail@jens-bissinger.de
161
+ executables:
162
+ - xploy
163
+ extensions: []
164
+ extra_rdoc_files: []
165
+ files:
166
+ - .coveralls.yml
167
+ - .gitignore
168
+ - .rspec
169
+ - .travis.yml
170
+ - Gemfile
171
+ - Guardfile
172
+ - LICENSE.txt
173
+ - README.md
174
+ - Rakefile
175
+ - appway-example/.xploy
176
+ - appway-example/appway-example.json
177
+ - bin/xploy
178
+ - lib/xploy.rb
179
+ - lib/xploy/api.rb
180
+ - lib/xploy/api/endpoints.rb
181
+ - lib/xploy/api/http.rb
182
+ - lib/xploy/api/request.rb
183
+ - lib/xploy/api/request/body.rb
184
+ - lib/xploy/cli.rb
185
+ - lib/xploy/error.rb
186
+ - lib/xploy/parameter.rb
187
+ - lib/xploy/version.rb
188
+ - spec/assets/appway-example.json
189
+ - spec/assets/custom.xway
190
+ - spec/integration/bin/xploy_binary_spec.rb
191
+ - spec/integration/lib/xploy/cli_spec.rb
192
+ - spec/lib/xploy/api/endpoints_spec.rb
193
+ - spec/lib/xploy/api/http_spec.rb
194
+ - spec/lib/xploy/api/request/body_spec.rb
195
+ - spec/lib/xploy/api/request_spec.rb
196
+ - spec/lib/xploy/api_spec.rb
197
+ - spec/lib/xploy/cli_spec.rb
198
+ - spec/lib/xploy/error_spec.rb
199
+ - spec/lib/xploy/parameter_spec.rb
200
+ - spec/lib/xploy_spec.rb
201
+ - spec/spec_helper.rb
202
+ - xploy.gemspec
203
+ homepage: ''
204
+ licenses: []
205
+ post_install_message:
206
+ rdoc_options: []
207
+ require_paths:
208
+ - lib
209
+ required_ruby_version: !ruby/object:Gem::Requirement
210
+ none: false
211
+ requirements:
212
+ - - ! '>='
213
+ - !ruby/object:Gem::Version
214
+ version: '0'
215
+ required_rubygems_version: !ruby/object:Gem::Requirement
216
+ none: false
217
+ requirements:
218
+ - - ! '>'
219
+ - !ruby/object:Gem::Version
220
+ version: 1.3.1
221
+ requirements: []
222
+ rubyforge_project:
223
+ rubygems_version: 1.8.23
224
+ signing_key:
225
+ specification_version: 3
226
+ summary: Provides the xploy command. See github.com/threez/appway.
227
+ test_files:
228
+ - spec/assets/appway-example.json
229
+ - spec/assets/custom.xway
230
+ - spec/integration/bin/xploy_binary_spec.rb
231
+ - spec/integration/lib/xploy/cli_spec.rb
232
+ - spec/lib/xploy/api/endpoints_spec.rb
233
+ - spec/lib/xploy/api/http_spec.rb
234
+ - spec/lib/xploy/api/request/body_spec.rb
235
+ - spec/lib/xploy/api/request_spec.rb
236
+ - spec/lib/xploy/api_spec.rb
237
+ - spec/lib/xploy/cli_spec.rb
238
+ - spec/lib/xploy/error_spec.rb
239
+ - spec/lib/xploy/parameter_spec.rb
240
+ - spec/lib/xploy_spec.rb
241
+ - spec/spec_helper.rb
242
+ has_rdoc: