jsduck 5.0.0.beta01 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.travis.yml +1 -0
  2. data/README.md +6 -32
  3. data/Rakefile +5 -5
  4. data/bin/jsduck +0 -1
  5. data/js-classes/String.js +3 -5
  6. data/jsduck.gemspec +3 -2
  7. data/lib/jsduck/aggregator.rb +1 -3
  8. data/lib/jsduck/app.rb +2 -2
  9. data/lib/jsduck/categories/file.rb +0 -6
  10. data/lib/jsduck/class.rb +1 -2
  11. data/lib/jsduck/doc/parser.rb +12 -5
  12. data/lib/jsduck/doc/scanner.rb +6 -0
  13. data/lib/jsduck/doc/standard_tag_parser.rb +10 -5
  14. data/lib/jsduck/doc/subproperties.rb +9 -2
  15. data/lib/jsduck/docs_code_comparer.rb +20 -7
  16. data/lib/jsduck/exporter/app.rb +18 -13
  17. data/lib/jsduck/exporter/full.rb +18 -21
  18. data/lib/jsduck/format/doc.rb +0 -1
  19. data/lib/jsduck/format/html_stack.rb +1 -2
  20. data/lib/jsduck/format/subproperties.rb +2 -2
  21. data/lib/jsduck/inline/auto_link.rb +1 -1
  22. data/lib/jsduck/inline/img.rb +1 -1
  23. data/lib/jsduck/inline/link.rb +4 -6
  24. data/lib/jsduck/inline/video.rb +1 -2
  25. data/lib/jsduck/js/ast.rb +1 -1
  26. data/lib/jsduck/js/esprima.rb +24 -9
  27. data/lib/jsduck/logger.rb +50 -12
  28. data/lib/jsduck/members_index.rb +1 -2
  29. data/lib/jsduck/merger.rb +20 -2
  30. data/lib/jsduck/options.rb +125 -24
  31. data/lib/jsduck/process/accessors.rb +21 -8
  32. data/lib/jsduck/process/enums.rb +2 -3
  33. data/lib/jsduck/process/ext4_events.rb +2 -1
  34. data/lib/jsduck/process/global_members.rb +1 -2
  35. data/lib/jsduck/process/importer.rb +2 -6
  36. data/lib/jsduck/process/inherit_class.rb +58 -0
  37. data/lib/jsduck/process/inherit_doc.rb +6 -175
  38. data/lib/jsduck/process/inherit_members.rb +257 -0
  39. data/lib/jsduck/process/lint.rb +18 -7
  40. data/lib/jsduck/process/overrides.rb +1 -2
  41. data/lib/jsduck/render/class.rb +1 -1
  42. data/lib/jsduck/tag/alias.rb +2 -1
  43. data/lib/jsduck/tag/alternate_class_names.rb +1 -0
  44. data/lib/jsduck/tag/aside.rb +3 -3
  45. data/lib/jsduck/tag/author.rb +18 -3
  46. data/lib/jsduck/tag/autodetected.rb +21 -0
  47. data/lib/jsduck/tag/boolean_tag.rb +1 -1
  48. data/lib/jsduck/tag/cfg.rb +7 -3
  49. data/lib/jsduck/tag/class.rb +1 -1
  50. data/lib/jsduck/tag/class_list_tag.rb +1 -1
  51. data/lib/jsduck/tag/constructor.rb +1 -1
  52. data/lib/jsduck/tag/css_var.rb +1 -1
  53. data/lib/jsduck/tag/default.rb +1 -1
  54. data/lib/jsduck/tag/deprecated_tag.rb +1 -1
  55. data/lib/jsduck/tag/docauthor.rb +2 -0
  56. data/lib/jsduck/tag/enum.rb +2 -2
  57. data/lib/jsduck/tag/event.rb +1 -1
  58. data/lib/jsduck/tag/extends.rb +1 -1
  59. data/lib/jsduck/tag/ftype.rb +2 -1
  60. data/lib/jsduck/tag/inheritdoc.rb +1 -1
  61. data/lib/jsduck/tag/localdoc.rb +33 -0
  62. data/lib/jsduck/tag/markdown.rb +1 -1
  63. data/lib/jsduck/tag/member.rb +1 -1
  64. data/lib/jsduck/tag/method.rb +1 -1
  65. data/lib/jsduck/tag/mixins.rb +1 -0
  66. data/lib/jsduck/tag/override.rb +1 -1
  67. data/lib/jsduck/tag/param.rb +16 -5
  68. data/lib/jsduck/tag/preventable.rb +1 -1
  69. data/lib/jsduck/tag/property.rb +7 -3
  70. data/lib/jsduck/tag/ptype.rb +2 -1
  71. data/lib/jsduck/tag/requires.rb +1 -0
  72. data/lib/jsduck/tag/return.rb +2 -1
  73. data/lib/jsduck/tag/since.rb +1 -5
  74. data/lib/jsduck/tag/tag.rb +21 -12
  75. data/lib/jsduck/tag/throws.rb +2 -1
  76. data/lib/jsduck/tag/type.rb +2 -2
  77. data/lib/jsduck/tag/uses.rb +1 -0
  78. data/lib/jsduck/tag/xtype.rb +2 -1
  79. data/lib/jsduck/tag_loader.rb +26 -15
  80. data/lib/jsduck/tag_registry.rb +20 -11
  81. data/lib/jsduck/web/css.rb +22 -0
  82. data/lib/jsduck/web/data.rb +50 -0
  83. data/lib/jsduck/web/icons.rb +31 -0
  84. data/lib/jsduck/web/index_html.rb +88 -0
  85. data/lib/jsduck/web/search.rb +148 -0
  86. data/lib/jsduck/{source/writer.rb → web/source.rb} +2 -2
  87. data/lib/jsduck/web/template.rb +52 -0
  88. data/lib/jsduck/web/writer.rb +84 -0
  89. metadata +513 -488
  90. data/lib/jsduck/app_data.rb +0 -41
  91. data/lib/jsduck/icons.rb +0 -29
  92. data/lib/jsduck/index_html.rb +0 -84
  93. data/lib/jsduck/search_data.rb +0 -146
  94. data/lib/jsduck/template_dir.rb +0 -50
  95. data/lib/jsduck/web_writer.rb +0 -87
data/.travis.yml CHANGED
@@ -6,6 +6,7 @@ install:
6
6
  - gem install rdiscount
7
7
  - gem install json
8
8
  - gem install parallel
9
+ - gem install execjs
9
10
  - gem install therubyracer -v 0.10.1
10
11
  - gem install rspec
11
12
  - gem install rake
data/README.md CHANGED
@@ -34,38 +34,12 @@ Standard rubygems install should do:
34
34
 
35
35
  $ [sudo] gem install jsduck
36
36
 
37
- If you encounter errors during gem installation, you may need to
38
- install the header files for compiling extension modules for Ruby 1.9.
39
- For Debian systems you'll need the `ruby1.9-dev` package. For Red Hat
40
- / CentOS / Fedora use the `ruby-devel` package.
41
-
42
- In **OSX Mountain Lion** the compilation of `therubyracer` dependency
43
- often fails for so far unknown reasons. Most users have found a
44
- solution in upgrading to Ruby 1.9 using [RVM][].
45
-
46
- For **Windows** users out there, you can download the binary version,
47
- which includes Ruby interpreter and all dependencies bundled in a
48
- single .exe file. Grab it from the [download page][].
49
-
50
- Alternatively you can install through rubygems, but you need to do
51
- some additional tweaks. First go and [download][libs download]
52
- therubyracer gem and v8 lib that stereobooster has built for
53
- windows. You need to install this special rubyracer version instead of
54
- the one from rubygems:
55
-
56
- > gem install therubyracer-0.11.0beta1-x86-mingw32.gem
57
-
58
- To make it actually work you need `v8.dll` somewhere in your
59
- system. Extract the `lib_v8.3.11.9.zip` take the `v8.dll` inside it
60
- and place into the `bin` directory of your Ruby installation (other
61
- dirs that are on your PATH can work too, but I've found this to be the
62
- most sensible place to put it). Now you're ready to install JSDuck:
63
-
64
- > gem install jsduck
65
-
66
- [RVM]: https://rvm.io/
67
- [download page]: https://sourceforge.net/projects/jsduck/files/
68
- [libs download]: https://github.com/stereobooster/therubyracer/downloads
37
+ Windows users probably want to [download a binary release][winbin].
38
+
39
+ See the [installation guide][] for help when you run into problems.
40
+
41
+ [winbin]: https://sourceforge.net/projects/jsduck/files/
42
+ [installation guide]: https://github.com/senchalabs/jsduck/wiki/Installation
69
43
 
70
44
  Usage
71
45
  -----
data/Rakefile CHANGED
@@ -104,7 +104,7 @@ def compress
104
104
  dir = "template-min"
105
105
 
106
106
  # Create JSB3 file for Docs app
107
- system("sencha", "create", "jsb", "-a", "http://localhost/~renesaarsoo/docs/", "-p", "#{dir}/app.jsb3")
107
+ system("sencha", "create", "jsb", "-a", "http://localhost/docs/", "-p", "#{dir}/app.jsb3")
108
108
  # Concatenate files listed in JSB3 file
109
109
  system("sencha", "build", "-p", "#{dir}/app.jsb3", "-d", dir)
110
110
 
@@ -247,7 +247,7 @@ task :ext4 => :sass do
247
247
  runner.add_debug
248
248
  runner.run
249
249
 
250
- system("cp -r #{EXT_BUILD} #{OUT_DIR}/extjs-build")
250
+ system("ln -s #{EXT_BUILD} #{OUT_DIR}/extjs-build")
251
251
  end
252
252
 
253
253
  desc "Run JSDuck on Ext JS from SDK repo (for internal use at Sencha)"
@@ -257,7 +257,7 @@ task :sdk => :sass do
257
257
  "--output", OUT_DIR,
258
258
  "--config", "#{SDK_DIR}/extjs/docs/config.json",
259
259
  "--examples-base-url", "extjs-build/examples/",
260
- # "--import", "4.1:../docs.sencha.com/exports/extjs-4.1.1",
260
+ # "--import", "4.1.3:../docs.sencha.com/exports/extjs-4.1.3",
261
261
  # "--import", "4.2",
262
262
  "--seo"
263
263
  )
@@ -265,7 +265,7 @@ task :sdk => :sass do
265
265
  runner.add_comments('ext-js', '4')
266
266
  runner.run
267
267
 
268
- system("cp -r #{EXT_BUILD} #{OUT_DIR}/extjs-build")
268
+ system("ln -s #{EXT_BUILD} #{OUT_DIR}/extjs-build")
269
269
  end
270
270
 
271
271
  desc "Run JSDuck on Sencha Touch 2 repo (for internal use at Sencha)"
@@ -286,7 +286,7 @@ task :touch2 => :sass do
286
286
  runner.add_comments('touch', '2')
287
287
  runner.run
288
288
 
289
- system("cp -r #{TOUCH_BUILD} #{OUT_DIR}/touch-build")
289
+ system("ln -s #{TOUCH_BUILD} #{OUT_DIR}/touch-build")
290
290
  end
291
291
 
292
292
  task :default => :spec
data/bin/jsduck CHANGED
@@ -17,7 +17,6 @@
17
17
  # For running when gem not installed
18
18
  $:.unshift File.dirname(File.dirname(__FILE__)) + "/lib"
19
19
 
20
- require 'rubygems'
21
20
  require 'jsduck/app'
22
21
  require 'jsduck/options'
23
22
 
data/js-classes/String.js CHANGED
@@ -125,7 +125,7 @@
125
125
  *
126
126
  * String.fromCharCode(65,66,67)
127
127
  *
128
- * @param {Number} num1, ..., numN A sequence of numbers that are Unicode values.
128
+ * @param {Number...} numbers A sequence of numbers that are Unicode values.
129
129
  * @return {String} String containing characters from encoding.
130
130
  */
131
131
 
@@ -359,9 +359,7 @@
359
359
 
360
360
  /**
361
361
  * @method concat
362
- * Combines the text of two strings and returns a new string.
363
- *
364
- * `concat` combines the text from one or more strings and returns a new string. Changes to the text in
362
+ * Combines combines the text from one or more strings and returns a new string. Changes to the text in
365
363
  * one string do not affect the other string.
366
364
  *
367
365
  * The following example combines strings into a new string.
@@ -369,7 +367,7 @@
369
367
  * var hello = "Hello, ";
370
368
  * console.log(hello.concat("Kevin", " have a nice day.")); // Hello, Kevin have a nice day.
371
369
  *
372
- * @param {String} string2...stringN
370
+ * @param {String...} strings The strings to concatenate.
373
371
  * @return {String} Result of both strings.
374
372
  */
375
373
 
data/jsduck.gemspec CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.required_rubygems_version = ">= 1.3.5"
3
3
 
4
4
  s.name = 'jsduck'
5
- s.version = '5.0.0.beta01'
6
- s.date = '2013-02-25'
5
+ s.version = '5.0.0.beta2'
6
+ s.date = Time.new.strftime('%Y-%m-%d')
7
7
  s.summary = "Simple JavaScript Duckumentation generator"
8
8
  s.description = "Documentation generator for Sencha JS frameworks"
9
9
  s.homepage = "https://github.com/senchalabs/jsduck"
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_dependency 'rdiscount'
23
23
  s.add_dependency 'json'
24
24
  s.add_dependency 'parallel'
25
+ s.add_dependency 'execjs'
25
26
  s.add_dependency 'therubyracer', '>= 0.10.0', '< 0.11.0'
26
27
  s.add_dependency 'dimensions'
27
28
 
@@ -87,9 +87,7 @@ module JsDuck
87
87
  end
88
88
 
89
89
  def warn_alt_name(cls)
90
- file = cls[:files][0][:filename]
91
- line = cls[:files][0][:linenr]
92
- Logger.warn(:alt_name, "Name #{cls[:name]} used as both classname and alternate classname", file, line)
90
+ Logger.warn(:alt_name, "Name #{cls[:name]} used as both classname and alternate classname", cls[:files][0])
93
91
  end
94
92
 
95
93
  # Merges new class-doc into old one.
data/lib/jsduck/app.rb CHANGED
@@ -3,7 +3,7 @@ require 'jsduck/batch_processor'
3
3
  require 'jsduck/assets'
4
4
  require 'jsduck/tag_registry'
5
5
  require 'jsduck/export_writer'
6
- require 'jsduck/web_writer'
6
+ require 'jsduck/web/writer'
7
7
 
8
8
  module JsDuck
9
9
 
@@ -47,7 +47,7 @@ module JsDuck
47
47
  end
48
48
 
49
49
  def generate_web_page
50
- WebWriter.new(@relations, @assets, @parsed_files, @opts).write
50
+ Web::Writer.new(@relations, @assets, @parsed_files, @opts).write
51
51
  end
52
52
 
53
53
  end
@@ -15,12 +15,6 @@ module JsDuck
15
15
  def generate
16
16
  @categories = Util::Json.read(@filename)
17
17
 
18
- # Don't crash if old syntax is used.
19
- if @categories.is_a?(Hash) && @categories["categories"]
20
- Logger.warn(nil, 'Update categories file to contain just the array inside {"categories": [...]}', @filename)
21
- @categories = @categories["categories"]
22
- end
23
-
24
18
  # Perform expansion on all class names containing * wildcard
25
19
  @categories.each do |cat|
26
20
  cat["groups"].each do |group|
data/lib/jsduck/class.rb CHANGED
@@ -115,8 +115,7 @@ module JsDuck
115
115
  # simplest thing and ignore it.
116
116
  Class.new({:name => classname}, false)
117
117
  else
118
- context = @doc[:files][0]
119
- Logger.warn(:extend, "Class #{classname} not found", context[:filename], context[:linenr])
118
+ Logger.warn(:extend, "Class #{classname} not found", @doc[:files][0])
120
119
  # Create placeholder class
121
120
  Class.new({:name => classname}, false)
122
121
  end
@@ -27,10 +27,10 @@ module JsDuck
27
27
  # JsDuck::DocFormatter.
28
28
  #
29
29
  class Parser < Doc::Scanner
30
- def parse(input, filename="", linenr=0)
31
- @filename = filename
32
- @linenr = linenr
30
+ def parse(input, filename="", linenr = 0)
31
+ @position = {:filename => filename, :linenr => linenr}
33
32
  @tags = []
33
+ @non_repeatable_tags = {}
34
34
  @input = StringScanner.new(Doc::Comment.purify(input))
35
35
 
36
36
  parse_loop
@@ -86,16 +86,23 @@ module JsDuck
86
86
  match(/\w+/)
87
87
  hw # Skip the whitespace right after the tag.
88
88
 
89
- tags = tag.parse_doc(self)
89
+ tags = tag.parse_doc(self, @position)
90
90
  if tags.is_a?(Hash)
91
91
  add_tag(tags)
92
92
  elsif tags.is_a?(Array)
93
93
  tags.each {|t| add_tag(t) }
94
94
  end
95
95
 
96
+ if !tag.repeatable
97
+ if @non_repeatable_tags[name]
98
+ warn(:tag_repeated, "@#{name} tag can occur only once per doc-comment")
99
+ end
100
+ @non_repeatable_tags[name] = true
101
+ end
102
+
96
103
  skip_white
97
104
  else
98
- Logger.warn(:tag, "Unsupported tag: @#{name}", @filename, @linenr)
105
+ warn(:tag, "Unsupported tag: @#{name}")
99
106
  @multiline_tag[:doc] += "@"
100
107
  end
101
108
  end
@@ -16,6 +16,7 @@ module JsDuck
16
16
  @ident_chain_pattern = /[$\w-]+(\.[$\w-]+)*/
17
17
 
18
18
  @input = nil # set to StringScanner in subclass
19
+ @position = {} # set in subclass
19
20
  end
20
21
 
21
22
  # Provides access to StringScanner
@@ -70,6 +71,11 @@ module JsDuck
70
71
  self
71
72
  end
72
73
 
74
+ # Prints a warning message
75
+ def warn(type, msg)
76
+ Logger.warn(type, msg, @position)
77
+ end
78
+
73
79
  end
74
80
 
75
81
  end
@@ -30,6 +30,7 @@ module JsDuck
30
30
  # were specified and how their matching succeeded.
31
31
  #
32
32
  def parse(cfg)
33
+ @tagname = cfg[:tagname]
33
34
  tag = {:tagname => cfg[:tagname]}
34
35
  add_type(tag) if cfg[:type]
35
36
  add_name_with_default(tag) if cfg[:name]
@@ -61,7 +62,7 @@ module JsDuck
61
62
  optional = nil
62
63
  end
63
64
 
64
- match(/\}/)
65
+ match(/\}/) or warn("@#{@tagname} tag syntax: '}' expected")
65
66
 
66
67
  return {:type => name, :optional => optional}
67
68
  end
@@ -71,10 +72,10 @@ module JsDuck
71
72
  if hw.match(/\[/)
72
73
  tag[:name] = hw.ident_chain
73
74
  if hw.match(/=/)
74
- hw
75
- tag[:default] = default_value
76
- end
77
- hw.match(/\]/)
75
+ hw
76
+ tag[:default] = default_value
77
+ end
78
+ hw.match(/\]/) or warn("@#{@tagname} tag syntax: ']' expected")
78
79
  tag[:optional] = true
79
80
  else
80
81
  tag[:name] = hw.ident_chain
@@ -148,6 +149,10 @@ module JsDuck
148
149
  def hw
149
150
  @ds.hw
150
151
  end
152
+
153
+ def warn(msg)
154
+ @ds.warn(:tag_syntax, msg)
155
+ end
151
156
  end
152
157
 
153
158
  end
@@ -28,6 +28,8 @@ module JsDuck
28
28
  def nest(raw_items, pos)
29
29
  # First item can't be namespaced, if it is ignore the rest.
30
30
  if raw_items[0] && raw_items[0][:name] =~ /\./
31
+ warn(raw_items[0][:name], pos)
32
+ raw_items[0][:name].sub!(/\..*$/, '')
31
33
  return [raw_items[0]]
32
34
  end
33
35
 
@@ -47,8 +49,7 @@ module JsDuck
47
49
  parent[:properties] = [] unless parent[:properties]
48
50
  parent[:properties] << it
49
51
  else
50
- msg = "Ignoring subproperty #{$1}.#{$2}, no parent found with name '#{$1}'."
51
- Logger.warn(:subproperty, msg, pos[:filename], pos[:linenr])
52
+ warn("#{$1}.#{$2}", pos)
52
53
  end
53
54
  else
54
55
  items << it
@@ -58,6 +59,12 @@ module JsDuck
58
59
  return items
59
60
  end
60
61
 
62
+ def warn(name, pos)
63
+ parent = name.sub(/\.[^.]*$/, '')
64
+ msg = "Ignoring subproperty '#{name}' not parent found with name '#{parent}'."
65
+ Logger.warn(:subproperty, msg, pos)
66
+ end
67
+
61
68
  end
62
69
 
63
70
  end
@@ -7,16 +7,23 @@ module JsDuck
7
7
  class DocsCodeComparer
8
8
  include Util::Singleton
9
9
 
10
- # When docs has the key, returns value from there.
11
- # When code has the key and matches with docs, gets value from there.
12
- # Otherwise returns nil.
13
- def merge_if_matches(key, docs, code)
10
+ # Sets the value of a field in result hash based on its value in
11
+ # docs and code hashes.
12
+ #
13
+ # - When docs has the key, gets value from there.
14
+ #
15
+ # - When code has the key and matches with docs, gets value from
16
+ # there, and also remembers the fact that we're using
17
+ # auto-detected value by recording it in :autodetected field.
18
+ #
19
+ def merge_if_matches(h, key, docs, code)
14
20
  if docs[key]
15
- docs[key]
21
+ h[key] = docs[key]
16
22
  elsif code[key] && matches?(docs, code)
17
- code[key]
23
+ h[key] = code[key]
24
+ mark_autodetected(h, key)
18
25
  else
19
- nil
26
+ # nothing
20
27
  end
21
28
  end
22
29
 
@@ -26,6 +33,12 @@ module JsDuck
26
33
  return docs[:name] == nil || docs[:name] == code[:name]
27
34
  end
28
35
 
36
+ # Stores the key as flag into h[:autodetcted]
37
+ def mark_autodetected(h, key)
38
+ h[:autodetected] = {} unless h[:autodetected]
39
+ h[:autodetected][key] = true
40
+ end
41
+
29
42
  end
30
43
 
31
44
  end
@@ -6,17 +6,31 @@ module JsDuck
6
6
  module Exporter
7
7
 
8
8
  # Exports data for Docs app.
9
- class App < Full
9
+ class App
10
10
  def initialize(relations, opts)
11
- super(relations, opts)
11
+ @full_exporter = Exporter::Full.new(relations, opts)
12
+ @relations = relations
12
13
  @renderer = Render::Class.new(opts)
13
14
  end
14
15
 
15
16
  # Returns compacted class data hash which contains an additional
16
17
  # :html field with full HTML to show on class overview page.
17
18
  def export(cls)
18
- data = super(cls)
19
+ data = @full_exporter.export(cls)
20
+
21
+ data[:component] = cls.inherits_from?("Ext.Component")
22
+ data[:superclasses] = cls.superclasses.collect {|c| c[:name] }
23
+ data[:subclasses] = @relations.subclasses(cls).collect {|c| c[:name] }.sort
24
+ data[:mixedInto] = @relations.mixed_into(cls).collect {|c| c[:name] }.sort
25
+ data[:alternateClassNames] = cls[:alternateClassNames].sort if cls[:alternateClassNames]
26
+
27
+ data[:mixins] = cls.deps(:mixins).collect {|c| c[:name] }.sort
28
+ data[:parentMixins] = cls.parent_deps(:mixins).collect {|c| c[:name] }.sort
29
+ data[:requires] = cls.deps(:requires).collect {|c| c[:name] }.sort
30
+ data[:uses] = cls.deps(:uses).collect {|c| c[:name] }.sort
31
+
19
32
  data[:html] = @renderer.render(data)
33
+
20
34
  return compact(data)
21
35
  end
22
36
 
@@ -25,21 +39,12 @@ module JsDuck
25
39
  # removes extra data from export
26
40
  def compact(cls)
27
41
  cls.delete(:doc)
28
- cls[:members] = compact_members_group(cls[:members])
29
- cls[:statics] = compact_members_group(cls[:statics])
42
+ cls[:members] = cls[:members].map {|m| compact_member(m) }
30
43
  cls[:files] = compact_files(cls[:files])
31
44
  cls[:meta] = combine_meta(cls)
32
45
  cls
33
46
  end
34
47
 
35
- def compact_members_group(group)
36
- c_group = {}
37
- group.each_pair do |type, members|
38
- c_group[type] = members.map {|m| compact_member(m) }
39
- end
40
- c_group
41
- end
42
-
43
48
  def compact_member(m)
44
49
  m_copy = {}
45
50
  [:name, :tagname, :owner, :id].each do |key|