js2 0.0.9 → 0.0.10

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/Changelog CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.0.10
2
+ * fixed last child bug
3
+ * added more features to *.js.yml extension
4
+ * require array of files or classes (added before code in orig file)
5
+ * include array of files or classes (added after code in orig file)
6
+ * fixed bug regarding make_compilation feature
7
+
1
8
  == 0.0.9 2009-12-23
2
9
  * large 1st move to runtime js2
3
10
 
data/Manifest.txt CHANGED
@@ -28,5 +28,8 @@ lib/js2/sel_decorator.rb
28
28
  lib/js2/tokenizer.rl.erb
29
29
  lib/js2/tree.rb
30
30
  lib/js2/universe.rb
31
+ lib/js2/test/selenium.rb
32
+ lib/js2/test/selenium_element.rb
33
+ lib/js2/test/selenium_helper.rb
31
34
  bin/js2
32
35
  website/index.txt
data/bin/js2 CHANGED
@@ -35,6 +35,10 @@ op = OptionParser.new do |opts|
35
35
  options[:daemonize] = true
36
36
  end
37
37
 
38
+ opts.on("-y YAML_FILE", "--yml YAML_FILE", "Use yml config") do |f|
39
+ options[:yml] = f
40
+ end
41
+
38
42
  opts.on('-h', "--help", "Help Screen") do
39
43
  puts opts
40
44
  exit
@@ -65,6 +69,16 @@ if !dir && File.directory?("./public/javascripts")
65
69
  dir = "./public/javascripts"
66
70
  end
67
71
 
72
+ if options[:yml]
73
+ d = JS2::Processor.daemon_from_yaml(options[:yml])
74
+ d.run do
75
+ puts "Recompiled: " + Time.now.to_s
76
+ sleep 0.5
77
+ end
78
+
79
+ exit
80
+ end
81
+
68
82
  options[:js2_dir] ||= '.'
69
83
  options[:js2_haml_dir] ||= '.'
70
84
  options[:write_dir] ||= '.'
@@ -108,6 +108,7 @@ class JS2.SelMarker {
108
108
  var classname = klass['class'].className.match(/\.\w+$/)[0];
109
109
  return namespace + classname;
110
110
  } else {
111
+ if (!klass.className) return klass['class'].className;
111
112
  return klass.className;
112
113
  }
113
114
  }
@@ -1,4 +1,6 @@
1
1
  class JS2::FileHandler
2
+ attr_accessor :read_dir, :write_dir
3
+
2
4
  def initialize (params = {})
3
5
  @read_dir = params[:read_dir]
4
6
  @write_dir = params[:write_dir] || @read_dir
@@ -6,6 +8,7 @@ class JS2::FileHandler
6
8
  @view_dir = params[:view_dir] || @read_dir
7
9
  @yml_dir = params[:read_dir]
8
10
 
11
+ puts self.inspect
9
12
  @mtimes = Hash.new
10
13
  end
11
14
 
@@ -20,8 +23,8 @@ class JS2::FileHandler
20
23
  end
21
24
 
22
25
  def get_yml_files
23
- return [] unless @haml_dir
24
- return Dir.glob(@haml_dir + '/**/*/*.js2.yml') + Dir.glob(@haml_dir + '/*.js2.yml')
26
+ return [] unless @yml_dir
27
+ return Dir.glob(@yml_dir + '/**/*/*.js2.yml') + Dir.glob(@yml_dir + '/*.js2.yml')
25
28
  end
26
29
 
27
30
  def get_files
data/lib/js2/js2.js CHANGED
@@ -1,4 +1,6 @@
1
1
  (function (scope) {
2
+ if (scope.JS2) return;
3
+
2
4
  var JS2 = {};
3
5
  scope.JS2 = JS2;
4
6
 
@@ -1,4 +1,6 @@
1
1
  (function (scope) {
2
+ if (scope.JS2) return;
3
+
2
4
  var JS2 = {};
3
5
  scope.JS2 = JS2;
4
6
 
data/lib/js2/parser.rb CHANGED
@@ -1,11 +1,9 @@
1
-
2
1
  #line 1 "tokenizer.rl"
3
2
  # Somewhat based on http://www.mozilla.org/js/language/js20-2000-07/formal/lexer-grammar.html
4
3
  # Regular Expression Literals determined with these rules:
5
4
  # http://www.mozilla.org/js/language/js20-1999-03-25/tokens.html
6
5
 
7
-
8
- #line 414 "tokenizer.rl"
6
+ #line 414 "tokenizer.rl"
9
7
 
10
8
 
11
9
  require 'rubygems'
@@ -110,7 +108,7 @@ class JS2::Parser
110
108
 
111
109
 
112
110
 
113
- #line 114 "tokenizer.c"
111
+ #line 112 "tokenizer.c"
114
112
  static const unsigned char _dude_actions[] = {
115
113
  0, 1, 1, 1, 2, 1, 3, 1,
116
114
  4, 1, 5, 1, 6, 1, 7, 1,
@@ -2469,20 +2467,18 @@ static const int dude_error = -1;
2469
2467
 
2470
2468
  static const int dude_en_main = 452;
2471
2469
 
2472
-
2473
2470
  #line 518 "tokenizer.rl"
2474
2471
 
2475
- #line 2476 "tokenizer.c"
2472
+ #line 2473 "tokenizer.c"
2476
2473
  {
2477
2474
  cs = dude_start;
2478
2475
  ts = 0;
2479
2476
  te = 0;
2480
2477
  act = 0;
2481
2478
  }
2482
-
2483
2479
  #line 519 "tokenizer.rl"
2484
2480
 
2485
- #line 2486 "tokenizer.c"
2481
+ #line 2482 "tokenizer.c"
2486
2482
  {
2487
2483
  int _klen;
2488
2484
  unsigned int _trans;
@@ -2501,7 +2497,7 @@ _resume:
2501
2497
  #line 1 "tokenizer.rl"
2502
2498
  {ts = p;}
2503
2499
  break;
2504
- #line 2505 "tokenizer.c"
2500
+ #line 2501 "tokenizer.c"
2505
2501
  }
2506
2502
  }
2507
2503
 
@@ -3467,7 +3463,7 @@ _eof_trans:
3467
3463
  }
3468
3464
  }
3469
3465
  break;
3470
- #line 3471 "tokenizer.c"
3466
+ #line 3467 "tokenizer.c"
3471
3467
  }
3472
3468
  }
3473
3469
 
@@ -3480,7 +3476,7 @@ _again:
3480
3476
  #line 1 "tokenizer.rl"
3481
3477
  {ts = 0;}
3482
3478
  break;
3483
- #line 3484 "tokenizer.c"
3479
+ #line 3480 "tokenizer.c"
3484
3480
  }
3485
3481
  }
3486
3482
 
@@ -3496,7 +3492,6 @@ _again:
3496
3492
  }
3497
3493
 
3498
3494
  }
3499
-
3500
3495
  #line 520 "tokenizer.rl"
3501
3496
 
3502
3497
  if (curly_idx) curly_idx--;
data/lib/js2/replace.rb CHANGED
@@ -99,8 +99,8 @@ module JS2
99
99
  end
100
100
 
101
101
  replacer = Replacer.new
102
- template = ERB.new(File.read('tokenizer.rl.erb'), 0, "%<>")
102
+ template = ERB.new(File.read('./tokenizer.rl.erb'), 0, "%<>")
103
103
 
104
- File.open('tokenizer.rl', 'w') do |f|
104
+ File.open('./tokenizer.rl', 'w') do |f|
105
105
  f << template.result(binding)
106
106
  end
@@ -0,0 +1,119 @@
1
+ begin
2
+ require 'rubygems'
3
+ gem "selenium-client", ">=1.2.16"
4
+ require "selenium/client"
5
+ require "yaml"
6
+ require "json"
7
+
8
+
9
+ class JS2::Test::Selenium < Selenium::Client::Driver
10
+ attr_accessor :reference_dir, :child_selector
11
+
12
+ RESET_SEL_MARKER = "if (window.JS2) { window.USE_SEL_MARKER = window.JS2.SEL_MARKER; }"
13
+
14
+ class << self
15
+
16
+ def logger
17
+ return @logger || nil
18
+ end
19
+
20
+ def logger= (a_logger)
21
+ @logger = a_logger
22
+ end
23
+
24
+ def connect (config_file, env = nil)
25
+ config = YAML.load_file(config_file)
26
+ config = config[env] if env
27
+
28
+ reference_dir = config['reference_dir'] || './spec/js2refs'
29
+ config = config['selenium']
30
+ ret = self.new( :host => config['host'] || 'localhost',
31
+ :port => config['port'] || 4444,
32
+ :browser => config['browser'] || '*firefox',
33
+ :url => config['url'] || 'http://www.factual.com',
34
+
35
+ :timeout_in_second => config[:timeout] || 60,
36
+ :highlight_located_element => config['highlight'] || false,
37
+ :javascript_framework => config['framework'] || 'jquery' )
38
+
39
+ # hack to get an instance var in.
40
+ ret.reference_dir = reference_dir
41
+ ret.child_selector = nil
42
+ return ret
43
+ end
44
+ end
45
+
46
+ def get_sel_markers ()
47
+ json = self.execute("JS2.SEL_MARKER.toJson()")
48
+ struct = JSON.parse(json)
49
+ puts YAML::dump(struct)
50
+ end
51
+
52
+ def open!(uri)
53
+ ret = self.open(uri)
54
+ self.wait_for_page
55
+ self.set_sel_marker!
56
+ return ret
57
+ end
58
+
59
+ def execute (js)
60
+ puts js
61
+ return self.js_eval("window.eval(#{js.to_json});")
62
+ end
63
+
64
+ def wait_for_html(html, timeout=15000)
65
+ js = %{window.document.body.innerHTML.toLowerCase().replace(/[\"\']/g, '').indexOf(#{html.to_json.downcase}) >= 0;}
66
+ begin
67
+ self.wait_for_condition(js, timeout)
68
+ return true
69
+ rescue Selenium::CommandError
70
+ return false
71
+ end
72
+ end
73
+
74
+ def uri
75
+ return self.location.sub(%r|^https?://(.*)?/|, '/')
76
+ end
77
+
78
+ def get_helper (klass_name)
79
+ klass = klass_name
80
+ ref_file = %|#{@reference_dir}/#{klass_name}.yml|
81
+ lookup = if File.exist? ref_file
82
+ YAML.load_file(ref_file)
83
+ else
84
+ Hash.new
85
+ end
86
+
87
+ return JS2::Test::SeleniumHelper.new(klass_name, lookup, self)
88
+ end
89
+
90
+ def child_scope (selector)
91
+ js = "if (window.JS2) { window.USE_SEL_MARKER = window.JS2.SEL_MARKER."
92
+ js << selector.split('>').collect { |s| "children.#{s}" }.join('.')
93
+ js << '};'
94
+ @keep_selector = js
95
+ self.set_sel_marker!
96
+ yield()
97
+ @keep_selector = nil
98
+ self.set_sel_marker!
99
+ end
100
+
101
+ def set_sel_marker!
102
+ self.execute(@keep_selector || RESET_SEL_MARKER)
103
+ end
104
+
105
+ def scope(klass_name)
106
+ self.set_sel_marker!
107
+ helper = get_helper(klass_name)
108
+ if block_given?
109
+ yield(helper)
110
+ else
111
+ return helper
112
+ end
113
+ end
114
+
115
+ end
116
+
117
+ rescue Exception => e
118
+ warn "No support for selenium"
119
+ end
@@ -0,0 +1,234 @@
1
+ class JS2::Test::SeleniumElement
2
+ attr_accessor :options
3
+ QUICK_WAIT_FOR = 5
4
+ AJAX_TIME_OUT = 60
5
+ def initialize (sel, klass, key, selector = nil)
6
+ @sel = sel
7
+ @key = key
8
+ @klass = klass
9
+ @selector = selector
10
+ end
11
+
12
+ def log (str)
13
+ if JS2::Test::Selenium.logger
14
+ JS2::Test::Selenium.logger.info(str)
15
+ end
16
+ end
17
+
18
+ def raw
19
+ ret = self.class.new(@sel, @klass, @key, @selector)
20
+ ret.options[:raw] = true
21
+ return ret
22
+ end
23
+
24
+ def check
25
+ return execute('checked=true')
26
+ end
27
+
28
+ def uncheck
29
+ return execute('checked=false')
30
+ end
31
+
32
+ def checked?
33
+ return execute('checked')
34
+ end
35
+
36
+ def execute (str)
37
+ wait_for_ajax
38
+ return @sel.execute(get_js_locator() + '.' + str)
39
+ end
40
+
41
+ def visible?(wait_for = nil)
42
+ return self.check_attribute(':visible', true, wait_for);
43
+ end
44
+
45
+ def sel_visible?()
46
+ return @sel.visible?(get_ele())
47
+ end
48
+
49
+ def hidden?(wait_for = nil)
50
+ return self.check_attribute(':visible', false, wait_for);
51
+ end
52
+
53
+ def find (str, idx = 0)
54
+ selector = "window.$(#{get_js_locator()}).find(#{str.to_json})[#{idx}]"
55
+ return self.class.new(@sel, @klass, @key, selector)
56
+ end
57
+
58
+ def filter (str, idx = 0)
59
+ selector = "window.$(#{get_js_locator()}).filter(#{str.to_json})[#{idx}]"
60
+ return self.class.new(@sel, @klass, @key, selector)
61
+ end
62
+
63
+ def get (idx)
64
+ # when jqObjects in //@+
65
+ selector = "#{get_js_locator()}[#{idx}][0]"
66
+ return self.class.new(@sel, @klass, @key, selector)
67
+ end
68
+
69
+ def [] (idx)
70
+ selector = "#{get_js_locator()}[#{idx}]"
71
+ return self.class.new(@sel, @klass, @key, selector)
72
+ end
73
+
74
+ def attr (attr)
75
+ get_ele()
76
+ js = %{window.$(#{get_js_locator()}).attr(#{attr.to_json});}
77
+ return @sel.js_eval(js)
78
+ end
79
+
80
+ def jq_attr (attr)
81
+ return @sel.execute("window.$(#{get_js_locator()}).#{attr};")
82
+ end
83
+
84
+ def check_attribute (attr, val, wait_for = nil)
85
+ wait_for ||= QUICK_WAIT_FOR
86
+ get_ele()
87
+ js = %{window.$(#{get_js_locator()}).is('#{attr}') == #{val.to_json};}
88
+ begin
89
+ log "Waiting for #{@key}.#{attr} to be #{val.to_json}... Waiting #{wait_for} secs."
90
+ @sel.wait_for_condition(js, wait_for)
91
+ rescue Selenium::CommandError
92
+ return false
93
+ end
94
+
95
+ return true
96
+ end
97
+
98
+ def containsp? (str, timeout=QUICK_WAIT_FOR)
99
+ wait_for_ajax
100
+ js = %{window.$(#{get_js_locator()}).html().toLowerCase().replace(/[\"\']/g, '').indexOf(#{str.to_json.downcase}) >= 0;}
101
+ begin
102
+ @sel.wait_for_condition(js, timeout)
103
+ return true
104
+ rescue Selenium::CommandError
105
+ return false
106
+ end
107
+ end
108
+
109
+ def contains? (str,timeout=QUICK_WAIT_FOR)
110
+ wait_for_ajax
111
+ js = %{window.$(#{get_js_locator()}).html().indexOf(#{str.to_json}) >= 0;}
112
+ begin
113
+ @sel.wait_for_condition(js, timeout)
114
+ return true
115
+ rescue Selenium::CommandError
116
+ return false
117
+ end
118
+ end
119
+
120
+ def click (options = {})
121
+ @sel.click(get_ele(), options)
122
+ end
123
+
124
+ def text
125
+ return @sel.text(get_ele())
126
+ end
127
+
128
+ def get_value
129
+ return @sel.get_value(get_ele())
130
+ end
131
+
132
+ def click! (options = {})
133
+ @sel.click(get_ele(), options)
134
+ @sel.wait_for_page
135
+ end
136
+
137
+ def mouse_up ()
138
+ trigger("LEFT_MOUSE_UP")
139
+ end
140
+
141
+ def mouse_down ()
142
+ trigger("LEFT_MOUSE_DOWN")
143
+ end
144
+
145
+ def mouse_over
146
+ @sel.mouse_over(get_ele())
147
+ end
148
+
149
+ def mouse_out
150
+ @sel.mouse_out(get_ele())
151
+ end
152
+
153
+ def key_down(value)
154
+ @sel.key_down(get_ele(), value)
155
+ end
156
+
157
+ def type (*params)
158
+ elem = get_ele()
159
+ @sel.type(elem, *params)
160
+ @sel.fire_event(elem, 'blur')
161
+ end
162
+
163
+ def select(params)
164
+ elem = get_ele()
165
+ @sel.select(elem, params)
166
+ @sel.fire_event(elem, 'blur')
167
+ end
168
+
169
+ def html ()
170
+ wait_for_ajax
171
+ js = get_js_locator()
172
+ return @sel.js_eval("#{js}.innerHTML;")
173
+ end
174
+
175
+ private
176
+
177
+ def get_locator
178
+ js = get_js_locator()
179
+ dom_locator = "dom=#{js};"
180
+ return dom_locator
181
+ end
182
+
183
+ def get_ele
184
+ real_locator = get_locator()
185
+ wait_for_ele()
186
+
187
+ return real_locator
188
+ end
189
+
190
+ def get_js_locator
191
+ if @selector
192
+ return @selector
193
+ end
194
+
195
+ filtered_locator = @key.to_s
196
+ int = ''
197
+
198
+ if m = filtered_locator.match(/([^\[\]]+)(\[([^\]]*)\])/)
199
+ log "filtered: #{filtered_locator} #{m.inspect}"
200
+ filtered_locator = m[1]
201
+ int = ', ' + m[3] if m[3]
202
+ end
203
+
204
+ funct = 'getVal'
205
+ get_val = "window.USE_SEL_MARKER.#{funct}('#{@klass}', '#{filtered_locator}'#{int})"
206
+
207
+ return get_val
208
+ end
209
+
210
+ def wait_for_ele ()
211
+ real_locator = get_locator
212
+ puts "waiting for #{real_locator}"
213
+ wait_for_ajax
214
+ @sel.wait_for_condition real_locator, QUICK_WAIT_FOR
215
+ puts "found #{real_locator}"
216
+ end
217
+
218
+ def wait_for_ajax
219
+ @sel.wait_for_condition('selenium.browserbot.getCurrentWindow().jQuery.active == 0', AJAX_TIME_OUT)
220
+ end
221
+
222
+ def trigger (event)
223
+ get_ele()
224
+ @sel.execute("window.$(#{get_js_locator}).trigger(JS2.SEL_EVENTS.#{event})")
225
+ end
226
+
227
+ def method_missing (method, *arg)
228
+ if @sel.respond_to?(method)
229
+ return @sel.send(method, get_js_locator, *arg)
230
+ else
231
+ raise "Method missing: #{method}"
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,27 @@
1
+ class JS2::Test::SeleniumHelper
2
+ attr_accessor :klass, :lookup
3
+
4
+ def initialize (klass, lookup, sel)
5
+ @klass = klass
6
+ @lookup = lookup
7
+ @sel = sel
8
+ @all_eles = Hash.new
9
+ end
10
+
11
+ def [] (key, tail = nil)
12
+ key = key.to_s
13
+
14
+ return @all_eles[key] if @all_eles[key]
15
+
16
+ # TODO: support inheritence
17
+ #raise "invalid key #{key}" unless @lookup[key]
18
+
19
+ return @all_eles[key] = JS2::Test::SeleniumElement.new(@sel, @klass, key, tail)
20
+ end
21
+
22
+ def type_form (params = {})
23
+ params.each_pair do |k,v|
24
+ self[k].type(v)
25
+ end
26
+ end
27
+ end
data/lib/js2/tree.rb CHANGED
@@ -105,12 +105,23 @@ class JS2::Class < JS2::Node
105
105
  if @extends
106
106
  extends = "#{@name}.oo('extends', #{@extends});"
107
107
  end
108
+
108
109
 
109
- return %|JS2.OO.#{createName}("#{@name}"); #{extends} (function (K) {var self=K; var _super=JS2.OO['super']; #{@start}|
110
+ return %|JS2.OO.#{createName}("#{@name}"); #{extends} (function (K,Package) {var self=K; var _super=JS2.OO['super']; #{@start}|
110
111
  end
111
112
 
112
113
  def last_string (s)
113
- return s.to_s + ")(#{@name});"
114
+ pkg = @name.split(/\./)
115
+ pkg.pop()
116
+
117
+ if pkg.empty?
118
+ pkg = 'null'
119
+ else
120
+ pkg = pkg.join('.')
121
+ end
122
+
123
+ #pkg = pkg.length > 0 ? pkg : 'null'
124
+ return s.to_s + ")(#{@name}, #{pkg});"
114
125
  end
115
126
 
116
127
  def to_s
@@ -323,7 +334,7 @@ class JS2::ParserHelper
323
334
  @classes.push(new_node)
324
335
  end
325
336
 
326
- @stack.last.set_child(new_node)
337
+ @stack.last.set_child(new_node) if @stack.last
327
338
  @stack.push << new_node
328
339
  end
329
340
 
data/lib/js2/universe.rb CHANGED
@@ -66,8 +66,7 @@ class JS2::Universe
66
66
 
67
67
  def add_yml (yml, filename)
68
68
  yml.each_pair do |klass_name, config|
69
- template = config['template']
70
- next unless template
69
+ template = config['template'] || []
71
70
 
72
71
  trees = @tree_lookup[klass_name]
73
72
  filename = trees.first.filename
@@ -79,11 +78,34 @@ class JS2::Universe
79
78
  end
80
79
 
81
80
  if config['make_compilation']
82
- compilation = [ filename ]
81
+ compilation = []
82
+
83
+ if config['require']
84
+ compilation += config['require'].collect do |name|
85
+ # if class name
86
+ if @tree_lookup[name]
87
+ @tree_lookup[name].collect { |t| t.filename }
88
+
89
+ # assume its just a file name
90
+ else
91
+ [ @file_handler.write_dir + '/' + name ]
92
+ end
93
+ end.flatten
94
+ end
83
95
 
84
- if config['include']
85
- compilation += config['include'].collect { |name| @tree_lookup[name].collect { |t| t.filename } }.flatten
96
+ compilation << filename
86
97
 
98
+ if config['include']
99
+ compilation += config['include'].collect do |name|
100
+ # if class name
101
+ if @tree_lookup[name]
102
+ @tree_lookup[name].collect { |t| t.filename }
103
+
104
+ # assume its just a file name
105
+ else
106
+ [ @file_handler.write_dir + '/' + name ]
107
+ end
108
+ end.flatten
87
109
  end
88
110
 
89
111
  template.each do |t|
data/lib/js2.rb CHANGED
@@ -3,7 +3,9 @@ $:.unshift(File.dirname(__FILE__)) unless
3
3
 
4
4
 
5
5
  module JS2
6
- VERSION = '0.0.9'
6
+ VERSION = '0.0.10'
7
+ module Test
8
+ end
7
9
  end
8
10
 
9
11
  require 'js2/config'
@@ -16,6 +18,10 @@ require 'js2/parser'
16
18
  require 'js2/file_handler'
17
19
  require 'js2/universe'
18
20
 
21
+ require 'js2/test/selenium'
22
+ require 'js2/test/selenium_element'
23
+ require 'js2/test/selenium_helper'
24
+
19
25
  $:.unshift File.dirname(__FILE__)
20
26
 
21
27
  begin
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: js2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Su
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-29 00:00:00 +08:00
12
+ date: 2010-02-19 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -86,6 +86,9 @@ files:
86
86
  - lib/js2/tokenizer.rl.erb
87
87
  - lib/js2/tree.rb
88
88
  - lib/js2/universe.rb
89
+ - lib/js2/test/selenium.rb
90
+ - lib/js2/test/selenium_element.rb
91
+ - lib/js2/test/selenium_helper.rb
89
92
  - bin/js2
90
93
  - website/index.txt
91
94
  has_rdoc: true