team_api 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9dff193621885b732e86f4796e1392e51f1d9025
4
- data.tar.gz: b28833e5d8d114604ba3f2b76e0af14d132f842a
3
+ metadata.gz: 09cb397221c7524d2b837eb6d94b5a980b589688
4
+ data.tar.gz: 1310a085598f49a6ef07be1a6d126420745a471e
5
5
  SHA512:
6
- metadata.gz: 694d28f524bb623a665ea18b821d08966b0abcd4dcf8d23949bfc491c319e3ae0df785ba9dcb34e362813e23da0539a000d9a77ad4b44b168867028d430c1488
7
- data.tar.gz: 38e1984b4bc4158481a5be7bd588b6c275b75156eadd32f73b77a392eb4fff2e12790f5448080aaf82a030e63de6b6a193af27d59b464fa5581f9f090594074d
6
+ metadata.gz: 7a74743fc65e50316b66dba13f061d9eef35cae2985be85ce2a87109a0649f9ef0e3898689c7a694e0fbe8b6cedfdc1f22f65d60c65b440aa7a840172d116a39
7
+ data.tar.gz: 2bbe5b4ea8be6e3a7a2de7b89387e776a029d8bc08610dd06b78d2e03e5ff40c9eb955c7ccaec57996c9a29d5133b2be173324c56c4e43753315209c1c3f7c20
@@ -1,69 +1,19 @@
1
1
  # @author Mike Bland (michael.bland@gsa.gov)
2
2
 
3
- require_relative 'config'
3
+ require_relative 'collection_canonicalizer'
4
4
  require_relative 'cross_referencer'
5
+ require_relative 'name_canonicalizer'
6
+ require_relative 'tag_canonicalizer'
7
+
8
+ require 'lambda_map_reduce'
5
9
 
6
10
  module TeamApi
7
11
  # Contains utility functions for canonicalizing names and the order of data.
8
12
  class Canonicalizer
9
13
  # Canonicalizes the order and names of certain fields within site_data.
10
14
  def self.canonicalize_data(site_data)
11
- sort_collections site_data
12
- %w(skills interests).each do |category|
13
- xrefs = site_data[category]
14
- canonicalize_tag_category xrefs
15
- site_data['team'].values.each do |member|
16
- canonicalize_tags_for_item category, xrefs, member
17
- end
18
- end
19
- end
20
-
21
- def self.sort_collections(site_data)
22
- Config.endpoint_config.each do |endpoint_info|
23
- collection = endpoint_info['collection']
24
- next unless site_data.member? collection
25
- sorted = sort_collection_values(endpoint_info,
26
- site_data[collection].values)
27
- sort_item_xrefs endpoint_info, sorted
28
- item_id_field = endpoint_info['item_id']
29
- site_data[collection] = sorted.map { |i| [i[item_id_field], i] }.to_h
30
- end
31
- end
32
-
33
- def self.sort_collection_values(endpoint_info, values)
34
- sort_by_field = endpoint_info['sort_by']
35
- if sort_by_field == 'last_name'
36
- sort_by_last_name values
37
- else
38
- values.sort_by { |i| (i[sort_by_field] || '').downcase }
39
- end
40
- end
41
- private_class_method :sort_collection_values
42
-
43
- def self.sort_item_xrefs(endpoint_info, collection)
44
- collection.each do |item|
45
- sortable_item_fields(item, endpoint_info).each do |field, field_info|
46
- item[field] = sort_collection_values field_info, item[field]
47
- end
48
- end
49
- end
50
- private_class_method :sort_item_xrefs
51
-
52
- def self.sortable_item_fields(item, collection_endpoint_info)
53
- collection_endpoint_info['item_collections'].map do |item_spec|
54
- field, endpoint_info = parse_collection_spec item_spec
55
- [field, endpoint_info] if item[field]
56
- end.compact
57
- end
58
- private_class_method :sortable_item_fields
59
-
60
- def self.parse_collection_spec(collection_spec)
61
- if collection_spec.instance_of? Hash
62
- [collection_spec['field'],
63
- Config.endpoint_info_by_collection[collection_spec['collection']]]
64
- else
65
- [collection_spec, Config.endpoint_info_by_collection[collection_spec]]
66
- end
15
+ CollectionCanonicalizer.sort_collections site_data
16
+ TagCanonicalizer.canonicalize_categories site_data, %w(skills interests)
67
17
  end
68
18
 
69
19
  # Returns a canonicalized, URL-friendly substitute for an arbitrary string.
@@ -72,32 +22,13 @@ module TeamApi
72
22
  s.downcase.gsub(/\s+/, '-')
73
23
  end
74
24
 
75
- def self.comparable_name(person)
76
- if person['last_name']
77
- [person['last_name'].downcase, person['first_name'].downcase]
78
- else
79
- # Trim off title suffix, if any.
80
- full_name = person['full_name'].downcase.split(',')[0]
81
- last_name = full_name.split.last
82
- [last_name, full_name]
83
- end
84
- end
85
- private_class_method :comparable_name
86
-
87
- # Sorts an array of team member data hashes based on the team members'
88
- # last names.
89
- # +team+:: An array of team member data hashes
90
- def self.sort_by_last_name(team)
91
- team.sort_by { |member| comparable_name member }
92
- end
93
-
94
25
  def self.team_xrefs(team, usernames)
95
26
  fields = CrossReferencer::TEAM_FIELDS
96
- usernames
27
+ result = usernames
97
28
  .map { |username| team[username] }
98
29
  .compact
99
30
  .map { |member| member.select { |field, _| fields.include? field } }
100
- .sort_by { |member| comparable_name member }
31
+ NameCanonicalizer.sort_by_last_name result
101
32
  end
102
33
 
103
34
  # Breaks a YYYYMMDD timestamp into a hyphenated version: YYYY-MM-DD
@@ -105,32 +36,5 @@ module TeamApi
105
36
  def self.hyphenate_yyyymmdd(timestamp)
106
37
  "#{timestamp[0..3]}-#{timestamp[4..5]}-#{timestamp[6..7]}"
107
38
  end
108
-
109
- # Consolidate tags entries that are not exactly the same. Selects the
110
- # lexicographically smaller version of the tag as a standard.
111
- #
112
- # In the future, we may just consider raising an error if there are two
113
- # different strings for the same thing.
114
- def self.canonicalize_tag_category(tags_xrefs)
115
- return if tags_xrefs.nil? || tags_xrefs.empty?
116
- tags_xrefs.replace(CrossReferencer.map_reduce(tags_xrefs.values,
117
- ->(xref) { [[xref['slug'], xref]] }, method(:consolidate_xrefs)).to_h)
118
- end
119
-
120
- def self.consolidate_xrefs(slug, xrefs)
121
- xrefs.sort_by! { |xref| xref['name'] }
122
- result = xrefs.each_with_object(xrefs.shift) do |xref, consolidated|
123
- consolidated['members'].concat xref['members']
124
- end
125
- result['members'].sort_by! { |member| comparable_name member }
126
- [slug, result]
127
- end
128
- private_class_method :consolidate_xrefs
129
-
130
- def self.canonicalize_tags_for_item(category, xrefs, item)
131
- return if item[category].nil?
132
- item[category].each { |tag| tag['name'] = xrefs[tag['slug']]['name'] }
133
- .sort_by! { |tag| tag['name'] }
134
- end
135
39
  end
136
40
  end
@@ -0,0 +1,56 @@
1
+ # @author Mike Bland (michael.bland@gsa.gov)
2
+
3
+ require_relative 'config'
4
+ require_relative 'name_canonicalizer'
5
+
6
+ module TeamApi
7
+ class CollectionCanonicalizer
8
+ def self.sort_collections(site_data)
9
+ Config.endpoint_config.each do |endpoint_info|
10
+ collection = endpoint_info['collection']
11
+ next unless site_data.member? collection
12
+ sorted = sort_collection_values(endpoint_info,
13
+ site_data[collection].values)
14
+ sort_item_xrefs endpoint_info, sorted
15
+ item_id_field = endpoint_info['item_id']
16
+ site_data[collection] = sorted.map { |i| [i[item_id_field], i] }.to_h
17
+ end
18
+ end
19
+
20
+ def self.sort_collection_values(endpoint_info, values)
21
+ sort_by_field = endpoint_info['sort_by']
22
+ if sort_by_field == 'last_name'
23
+ NameCanonicalizer.sort_by_last_name values
24
+ else
25
+ values.sort_by { |i| (i[sort_by_field] || '').downcase }
26
+ end
27
+ end
28
+ private_class_method :sort_collection_values
29
+
30
+ def self.sort_item_xrefs(endpoint_info, collection)
31
+ collection.each do |item|
32
+ sortable_item_fields(item, endpoint_info).each do |field, field_info|
33
+ item[field] = sort_collection_values field_info, item[field]
34
+ end
35
+ end
36
+ end
37
+ private_class_method :sort_item_xrefs
38
+
39
+ def self.sortable_item_fields(item, collection_endpoint_info)
40
+ collection_endpoint_info['item_collections'].map do |item_spec|
41
+ field, endpoint_info = parse_collection_spec item_spec
42
+ [field, endpoint_info] if item[field]
43
+ end.compact
44
+ end
45
+ private_class_method :sortable_item_fields
46
+
47
+ def self.parse_collection_spec(collection_spec)
48
+ if collection_spec.instance_of? Hash
49
+ [collection_spec['field'],
50
+ Config.endpoint_info_by_collection[collection_spec['collection']]]
51
+ else
52
+ [collection_spec, Config.endpoint_info_by_collection[collection_spec]]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,106 @@
1
+ # @author Mike Bland (michael.bland@gsa.gov)
2
+
3
+ module TeamApi
4
+ # Signals that a cross-reference ID value in one object is not present in
5
+ # the target collection. Only raised in "private" mode, since "public" mode
6
+ # may legitimately filter out data.
7
+ class UnknownCrossReferenceTargetId < StandardError
8
+ end
9
+
10
+ # Provides a collection with the ability to replace identifiers with more
11
+ # detailed cross-reference values from another collection, and with the
12
+ # ability to construct its own cross-reference values to assign to values
13
+ # from other collections.
14
+ #
15
+ # The intent is to provide enough cross-reference information to surface in
16
+ # an API without requiring the client to join the data necessary to produce
17
+ # cross-links. For example, instead of surfacing `['mbland']` in a list of
18
+ # team members, this class will produce `[{'name' => 'mbland', 'full_name'
19
+ # => 'Mike Bland', 'first_name' => 'Mike', 'last_name' => 'Bland'}]`, which
20
+ # the client can use to more easily sort multiple values and transform into:
21
+ # `<a href="https://hub.18f.gov/team/mbland/">Mike Bland</a>`.
22
+ class CrossReferenceData
23
+ attr_accessor :collection_name, :data, :item_xref_fields, :public_mode
24
+
25
+ # @param site [Jekyll::Site] site object
26
+ # @param collection_name [String] name of collection within site.data
27
+ # @param field_to_xref [String] name of the field to cross-reference
28
+ # @param item_xref_fields [Array<String>] list of fields from which to
29
+ # produce cross-references for this collection
30
+ def initialize(site, collection_name, item_xref_fields)
31
+ @collection_name = collection_name
32
+ @data = site.data[collection_name] || {}
33
+ @item_xref_fields = item_xref_fields
34
+ @public_mode = site.config['public']
35
+ end
36
+
37
+ # Selects fields from `item` to produce a smaller hash as a
38
+ # cross-reference.
39
+ def item_to_xref(item)
40
+ item.select { |field, _| item_xref_fields.include? field }
41
+ end
42
+
43
+ # Translates identifiers into cross-reference values in both this object's
44
+ # collection and the `target` collection.
45
+ #
46
+ # This object's collection is considered the "source", and references to
47
+ # its values will be injected into "target". For each "source" object,
48
+ # `source[target.collection_name]` should be an existing field containing
49
+ # identifiers that are keys into `target.data`. The `target` collection
50
+ # values should not contain a `target[source.collection_name]` field; that
51
+ # field will be created by this method.
52
+ #
53
+ # @param target [CrossReferenceData] contains data to cross-reference with
54
+ # items from this object's collection
55
+ # @param source_to_target_field [String] if specified, the field from this
56
+ # collection's objects that contain identifiers of objects stored within
57
+ # target; if not specified, target.collection_name will be used instead
58
+ def create_xrefs(target, source_to_target_field: nil)
59
+ target_collection_field = source_to_target_field || target.collection_name
60
+ data.values.each do |source|
61
+ create_xrefs_for_source source, target_collection_field, target
62
+ end
63
+ target.data.values.each { |item| (item[collection_name] || []).uniq! }
64
+ end
65
+
66
+ private
67
+
68
+ def create_xrefs_for_source(source, target_collection_field, target)
69
+ source_xref = item_to_xref source
70
+ target_ids = filter_target_ids target, source, target_collection_field
71
+ link_source_to_targets source_xref, target_ids, target
72
+ source[target_collection_field] = target_xrefs target, target_ids
73
+ end
74
+
75
+ def filter_target_ids(target_xref, source_item, target_collection_field)
76
+ (source_item[target_collection_field] || []).map do |target_id|
77
+ if target_xref.data.member? target_id
78
+ target_id
79
+ elsif !public_mode
80
+ fail UnknownCrossReferenceTargetId, unknown_cross_reference_msg(
81
+ collection_name, source_item, target_collection_field,
82
+ target_xref, target_id)
83
+ end
84
+ end.compact
85
+ end
86
+
87
+ def unknown_cross_reference_msg(collection_name,
88
+ source_item, target_collection_field, target_xref, target_id)
89
+ "source collection: \"#{collection_name}\" " \
90
+ "source xref: #{item_to_xref source_item} " \
91
+ "target collection field: \"#{target_collection_field}\" " \
92
+ "target collection: \"#{target_xref.collection_name}\" " \
93
+ "target ID: \"#{target_id}\""
94
+ end
95
+
96
+ def link_source_to_targets(source_xref, target_ids, target_xref)
97
+ target_ids.each do |target_id|
98
+ (target_xref.data[target_id][collection_name] ||= []) << source_xref
99
+ end
100
+ end
101
+
102
+ def target_xrefs(target_xref, target_ids)
103
+ target_ids.map { |id| target_xref.item_to_xref target_xref.data[id] }
104
+ end
105
+ end
106
+ end
@@ -2,111 +2,12 @@
2
2
 
3
3
  require_relative 'api'
4
4
  require_relative 'canonicalizer'
5
+ require_relative 'cross_reference_data'
6
+ require_relative 'name_canonicalizer'
5
7
 
6
- module TeamApi
7
- # Signals that a cross-reference ID value in one object is not present in
8
- # the target collection. Only raised in "private" mode, since "public" mode
9
- # may legitimately filter out data.
10
- class UnknownCrossReferenceTargetId < StandardError
11
- end
12
-
13
- # Provides a collection with the ability to replace identifiers with more
14
- # detailed cross-reference values from another collection, and with the
15
- # ability to construct its own cross-reference values to assign to values
16
- # from other collections.
17
- #
18
- # The intent is to provide enough cross-reference information to surface in
19
- # an API without requiring the client to join the data necessary to produce
20
- # cross-links. For example, instead of surfacing `['mbland']` in a list of
21
- # team members, this class will produce `[{'name' => 'mbland', 'full_name'
22
- # => 'Mike Bland', 'first_name' => 'Mike', 'last_name' => 'Bland'}]`, which
23
- # the client can use to more easily sort multiple values and transform into:
24
- # `<a href="https://hub.18f.gov/team/mbland/">Mike Bland</a>`.
25
- class CrossReferenceData
26
- attr_accessor :collection_name, :data, :item_xref_fields, :public_mode
27
-
28
- # @param site [Jekyll::Site] site object
29
- # @param collection_name [String] name of collection within site.data
30
- # @param field_to_xref [String] name of the field to cross-reference
31
- # @param item_xref_fields [Array<String>] list of fields from which to
32
- # produce cross-references for this collection
33
- def initialize(site, collection_name, item_xref_fields)
34
- @collection_name = collection_name
35
- @data = site.data[collection_name] || {}
36
- @item_xref_fields = item_xref_fields
37
- @public_mode = site.config['public']
38
- end
39
-
40
- # Selects fields from `item` to produce a smaller hash as a
41
- # cross-reference.
42
- def item_to_xref(item)
43
- item.select { |field, _| item_xref_fields.include? field }
44
- end
45
-
46
- # Translates identifiers into cross-reference values in both this object's
47
- # collection and the `target` collection.
48
- #
49
- # This object's collection is considered the "source", and references to
50
- # its values will be injected into "target". For each "source" object,
51
- # `source[target.collection_name]` should be an existing field containing
52
- # identifiers that are keys into `target.data`. The `target` collection
53
- # values should not contain a `target[source.collection_name]` field; that
54
- # field will be created by this method.
55
- #
56
- # @param target [CrossReferenceData] contains data to cross-reference with
57
- # items from this object's collection
58
- # @param source_to_target_field [String] if specified, the field from this
59
- # collection's objects that contain identifiers of objects stored within
60
- # target; if not specified, target.collection_name will be used instead
61
- def create_xrefs(target, source_to_target_field: nil)
62
- target_collection_field = source_to_target_field || target.collection_name
63
- data.values.each do |source|
64
- create_xrefs_for_source source, target_collection_field, target
65
- end
66
- target.data.values.each { |item| (item[collection_name] || []).uniq! }
67
- end
68
-
69
- private
70
-
71
- def create_xrefs_for_source(source, target_collection_field, target)
72
- source_xref = item_to_xref source
73
- target_ids = filter_target_ids target, source, target_collection_field
74
- link_source_to_targets source_xref, target_ids, target
75
- source[target_collection_field] = target_xrefs target, target_ids
76
- end
77
-
78
- def filter_target_ids(target_xref, source_item, target_collection_field)
79
- (source_item[target_collection_field] || []).map do |target_id|
80
- if target_xref.data.member? target_id
81
- target_id
82
- elsif !public_mode
83
- fail UnknownCrossReferenceTargetId, unknown_cross_reference_msg(
84
- collection_name, source_item, target_collection_field,
85
- target_xref, target_id)
86
- end
87
- end.compact
88
- end
89
-
90
- def unknown_cross_reference_msg(collection_name,
91
- source_item, target_collection_field, target_xref, target_id)
92
- "source collection: \"#{collection_name}\" " \
93
- "source xref: #{item_to_xref source_item} " \
94
- "target collection field: \"#{target_collection_field}\" " \
95
- "target collection: \"#{target_xref.collection_name}\" " \
96
- "target ID: \"#{target_id}\""
97
- end
98
-
99
- def link_source_to_targets(source_xref, target_ids, target_xref)
100
- target_ids.each do |target_id|
101
- (target_xref.data[target_id][collection_name] ||= []) << source_xref
102
- end
103
- end
104
-
105
- def target_xrefs(target_xref, target_ids)
106
- target_ids.map { |id| target_xref.item_to_xref target_xref.data[id] }
107
- end
108
- end
8
+ require 'lambda_map_reduce'
109
9
 
10
+ module TeamApi
110
11
  # Builds cross-references between data sets.
111
12
  class CrossReferencer
112
13
  TEAM_FIELDS = %w(name last_name first_name full_name self)
@@ -158,24 +59,48 @@ module TeamApi
158
59
  end
159
60
  end
160
61
 
62
+ # Generates a Hash of { tag => cross-reference } generated from the tag
63
+ # `category` Arrays from each element of `items`.
64
+ #
65
+ # For example:
66
+ # TEAM = {
67
+ # 'mbland' => {
68
+ # 'name' => 'mbland', 'full_name' => 'Mike Bland',
69
+ # 'skills' => ['C++', 'Python'] },
70
+ # 'arowla' => {
71
+ # 'name' => 'arowla', 'full_name' => 'Alison Rowland',
72
+ # 'skills' => ['Python'] },
73
+ # }
74
+ # TEAM_XREF = CrossReferenceData.new site, 'team', ['name', 'full_name']
75
+ # create_tag_xrefs site, TEAM, 'skills', TEAM_XREF
76
+ #
77
+ # will produce:
78
+ # {'C++' => {
79
+ # 'name' => 'C++',
80
+ # 'slug' => 'c++',
81
+ # 'self' => 'https://.../skills/c++',
82
+ # 'members' => [{ 'name' => 'mbland', 'full_name' => 'Mike Bland' }],
83
+ # },
84
+ #
85
+ # 'Python' => {
86
+ # 'name' => 'Python',
87
+ # 'slug' => 'python',
88
+ # 'self' => 'https://.../skills/python',
89
+ # 'members' => [
90
+ # { 'name' => 'mbland', 'full_name' => 'Mike Bland' },
91
+ # { 'name' => 'arowla', 'full_name' => 'Alison Rowland' },
92
+ # ],
93
+ # },
94
+ # }
161
95
  def self.create_tag_xrefs(site, items, category, xref_data)
162
- map_items_to_tags = lambda do |item|
96
+ items_to_tags = lambda do |item|
163
97
  item_xref = xref_data.item_to_xref item
164
98
  item[category].map { |tag| [tag, item_xref] } unless item[category].nil?
165
99
  end
166
100
  create_tag_xrefs = lambda do |tag, item_xrefs|
167
101
  [tag, tag_xref(site, category, tag, item_xrefs)]
168
102
  end
169
- map_reduce(items, map_items_to_tags, create_tag_xrefs).to_h
170
- end
171
-
172
- # Returns an Array of objects after mapping and reducing items.
173
- # mapper takes a single item and returns an Array of [key, value] pairs.
174
- # reducer takes a [key, Array of values] pair and returns a single item.
175
- def self.map_reduce(items, mapper, reducer)
176
- items.flat_map { |item| mapper.call(item) }.compact
177
- .each_with_object({}) { |kv, shuffle| (shuffle[kv[0]] ||= []) << kv[1] }
178
- .map { |key, values| reducer.call(key, values) }.compact
103
+ LambdaMapReduce.map_reduce(items, items_to_tags, create_tag_xrefs).to_h
179
104
  end
180
105
 
181
106
  def self.tag_xref(site, category, tag, members)
@@ -184,7 +109,7 @@ module TeamApi
184
109
  { 'name' => tag,
185
110
  'slug' => tag_slug,
186
111
  'self' => File.join(Api.baseurl(site), category_slug, tag_slug),
187
- 'members' => Canonicalizer.sort_by_last_name(members || []),
112
+ 'members' => NameCanonicalizer.sort_by_last_name(members || []),
188
113
  }
189
114
  end
190
115
 
@@ -119,7 +119,7 @@ module TeamApi
119
119
 
120
120
  def team_member_from_reference(reference)
121
121
  key = (reference.instance_of? String) ? reference : (
122
- reference['email'] || reference['github'])
122
+ reference['id'] || reference['email'] || reference['github'])
123
123
  team[key] || team[team_by_email[key] || team_by_github[key]]
124
124
  end
125
125
 
@@ -0,0 +1,28 @@
1
+ # @author Mike Bland (michael.bland@gsa.gov)
2
+
3
+ module TeamApi
4
+ class NameCanonicalizer
5
+ # Sorts an array of team member data hashes based on the team members'
6
+ # last names.
7
+ # +team+:: An array of team member data hashes
8
+ def self.sort_by_last_name(team)
9
+ team.sort_by { |member| comparable_name member }
10
+ end
11
+
12
+ def self.sort_by_last_name!(team)
13
+ team.sort_by! { |member| comparable_name member }
14
+ end
15
+
16
+ def self.comparable_name(person)
17
+ if person['last_name']
18
+ [person['last_name'].downcase, person['first_name'].downcase]
19
+ else
20
+ # Trim off title suffix, if any.
21
+ full_name = person['full_name'].downcase.split(',')[0]
22
+ last_name = full_name.split.last
23
+ [last_name, full_name]
24
+ end
25
+ end
26
+ private_class_method :comparable_name
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ # @author Mike Bland (michael.bland@gsa.gov)
2
+
3
+ require_relative 'name_canonicalizer'
4
+
5
+ module TeamApi
6
+ # Contains utility functions for canonicalizing names and the order of data.
7
+ class TagCanonicalizer
8
+ def self.canonicalize_categories(site_data, tag_categories)
9
+ tag_categories.each do |category|
10
+ xrefs = site_data[category]
11
+ canonicalize_tag_category xrefs
12
+ site_data['team'].values.each do |member|
13
+ canonicalize_tags_for_item category, xrefs, member
14
+ end
15
+ end
16
+ end
17
+
18
+ # Consolidate tags entries that are not exactly the same. Selects the
19
+ # lexicographically smaller version of the tag as a standard.
20
+ #
21
+ # In the future, we may just consider raising an error if there are two
22
+ # different strings for the same thing.
23
+ def self.canonicalize_tag_category(tags_xrefs)
24
+ return if tags_xrefs.nil? || tags_xrefs.empty?
25
+ tags_xrefs.replace(LambdaMapReduce.map_reduce(
26
+ tags_xrefs.values,
27
+ ->(xref) { [[xref['slug'], xref]] },
28
+ method(:consolidate_xrefs)).to_h)
29
+ end
30
+
31
+ def self.consolidate_xrefs(slug, xrefs)
32
+ xrefs.sort_by! { |xref| xref['name'] }
33
+ result = xrefs.each_with_object(xrefs.shift) do |xref, consolidated|
34
+ consolidated['members'].concat xref['members']
35
+ end
36
+ NameCanonicalizer.sort_by_last_name! result['members']
37
+ [slug, result]
38
+ end
39
+ private_class_method :consolidate_xrefs
40
+
41
+ def self.canonicalize_tags_for_item(category, xrefs, item)
42
+ return if item[category].nil?
43
+ item[category].each { |tag| tag['name'] = xrefs[tag['slug']]['name'] }
44
+ .sort_by! { |tag| tag['name'] }
45
+ end
46
+ end
47
+ end
@@ -1,5 +1,5 @@
1
1
  # @author Mike Bland (michael.bland@gsa.gov)
2
2
 
3
3
  module TeamApi
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: team_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Bland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-15 00:00:00.000000000 Z
11
+ date: 2015-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: lambda_map_reduce
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: go_script
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -193,13 +207,17 @@ files:
193
207
  - lib/team_api/README.md
194
208
  - lib/team_api/api.rb
195
209
  - lib/team_api/canonicalizer.rb
210
+ - lib/team_api/collection_canonicalizer.rb
196
211
  - lib/team_api/config.rb
212
+ - lib/team_api/cross_reference_data.rb
197
213
  - lib/team_api/cross_referencer.rb
198
214
  - lib/team_api/endpoints.yml
199
215
  - lib/team_api/front_matter.rb
200
216
  - lib/team_api/generator.rb
201
217
  - lib/team_api/joiner.rb
218
+ - lib/team_api/name_canonicalizer.rb
202
219
  - lib/team_api/snippets.rb
220
+ - lib/team_api/tag_canonicalizer.rb
203
221
  - lib/team_api/tag_categories.yml
204
222
  - lib/team_api/version.rb
205
223
  homepage: https://github.com/18F/team_api