team_hub 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/lib/team_hub.rb +1 -0
- data/lib/team_hub/cross_referencer.rb +210 -0
- data/lib/team_hub/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06eccea8356a509dea1155db551c5ec3531fffd9
|
4
|
+
data.tar.gz: ff689d5c288d78cf8ee1a7dc71fae104b266bc86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61814aa3d81ec152e8aac2de632cedf351c16c2918486c3910435f3d556130e836088263d5445e7b75d19e0e0288b4287fc109e88a0274fd106d77f1bbeb9874
|
7
|
+
data.tar.gz: c78e5524d6489ea19f907a61d0e951baedd5849c08ad5ab6e16dde9c5184c0aeae1fbd5682542f0fc2612830f9c24f6c7d6083e551842aea9ecb96656650f877
|
data/lib/team_hub.rb
CHANGED
@@ -0,0 +1,210 @@
|
|
1
|
+
# team_hub - Components for creating a team Hub using Jekyll
|
2
|
+
#
|
3
|
+
# Written in 2015 by Mike Bland (michael.bland@gsa.gov)
|
4
|
+
# on behalf of the 18F team, part of the US General Services Administration:
|
5
|
+
# https://18f.gsa.gov/
|
6
|
+
#
|
7
|
+
# To the extent possible under law, the author(s) have dedicated all copyright
|
8
|
+
# and related and neighboring rights to this software to the public domain
|
9
|
+
# worldwide. This software is distributed without any warranty.
|
10
|
+
#
|
11
|
+
# You should have received a copy of the CC0 Public Domain Dedication along
|
12
|
+
# with this software. If not, see
|
13
|
+
# <https://creativecommons.org/publicdomain/zero/1.0/>.
|
14
|
+
#
|
15
|
+
# @author Mike Bland (michael.bland@gsa.gov)
|
16
|
+
|
17
|
+
module TeamHub
|
18
|
+
|
19
|
+
# Operations for creating cross-references between data objects
|
20
|
+
class CrossReferencer
|
21
|
+
# Creates an index of +collection+ items based on +key+.
|
22
|
+
#
|
23
|
+
# @param collection [Array<Hash>] collection from which to build an index
|
24
|
+
# @param key [String] property used to build the index
|
25
|
+
# @return [Hash]
|
26
|
+
def self.create_index(collection, key)
|
27
|
+
index = collection.group_by {|i| i[key]}
|
28
|
+
index.delete nil
|
29
|
+
index
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates cross-references between Hash objects in +sources+ and Hash
|
33
|
+
# objects in +targets+.
|
34
|
+
#
|
35
|
+
# Each object in +sources+ should have a +source_key+ member that is an
|
36
|
+
# Array<String> of target identifiers (keys into +targets+) that will be
|
37
|
+
# replaced with an Array<Hash> of object references from +targets+.
|
38
|
+
#
|
39
|
+
# Objects in +targets+ should not yet contain a +target_key+ member. This
|
40
|
+
# member will be created as an Array<Hash> of object references from
|
41
|
+
# +sources+.
|
42
|
+
#
|
43
|
+
# If an object cross-referenced by an object in +sources+ does not exist
|
44
|
+
# in +targets+, that cross-reference will be skipped without error. If an
|
45
|
+
# object from +sources+ does not contain a +source_key+ property, it will
|
46
|
+
# also be skipped.
|
47
|
+
#
|
48
|
+
# @param sources [Array<Hash>] objects containing the information
|
49
|
+
# needed to establish cross-references with objects in +targets+
|
50
|
+
# @param source_key [String] identifies the cross-referenced property from
|
51
|
+
# +sources+
|
52
|
+
# @param targets [Hash<Hash>] index of objects to cross-reference with
|
53
|
+
# objects from +sources+
|
54
|
+
# @param target_key [String] identifies the cross-referenced property from
|
55
|
+
# +targets+
|
56
|
+
def self.create_xrefs(sources, source_key, targets, target_key)
|
57
|
+
(sources || []).each do |source|
|
58
|
+
(source[source_key] || []).map! do |target_id|
|
59
|
+
target = targets[target_id]
|
60
|
+
(target[target_key] ||= Array.new) << source if target
|
61
|
+
target
|
62
|
+
end.compact!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Creates a copy of +collection+ where each item's +property+ member has
|
67
|
+
# had its objects replaced with an Array of +property_key+ values.
|
68
|
+
#
|
69
|
+
# The primary use case is to "flatten" a list of Hash objects that have
|
70
|
+
# cross-reference links back to Hash objects in +collection+. While
|
71
|
+
# cross-referencing objects makes it easy to traverse the object graph
|
72
|
+
# in-memory, it is useful to flatten these cross-references when
|
73
|
+
# serializing data, generating API data, checking cross-references in
|
74
|
+
# automated tests (in concert with property_map, to avoid producing
|
75
|
+
# voluminous output for assertion failures due to extraneous data and the
|
76
|
+
# infinite recursion between cross-referenced objects), logging, and error
|
77
|
+
# reporting.
|
78
|
+
#
|
79
|
+
# @param collection [Array<Hash>] objects for which to flatten the
|
80
|
+
# +property+ collection
|
81
|
+
# @param property [String] property of objects in +collection+
|
82
|
+
# corresponding to a list of (possibly cross-referenced) Hash objects
|
83
|
+
# @param property_key [String] primary key of cross-referenced objects;
|
84
|
+
# the corresponding value should be a String
|
85
|
+
# @return [Array] a copy of collection with +property+ items flattened
|
86
|
+
def self.flatten_property(collection, property, property_key)
|
87
|
+
collection.map do |i|
|
88
|
+
item = i.clone
|
89
|
+
item[property] = i[property].map {|p| p[property_key]} if i[property]
|
90
|
+
item
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# The in-place version of flatten_property which directly replaces the
|
95
|
+
# +property+ member of each item of +collection+ with an Array of
|
96
|
+
# +property_key+ values.
|
97
|
+
#
|
98
|
+
# In addition to the use cases described in the the flatten_property
|
99
|
+
# comment, the in-place version may be useful to help free memory by
|
100
|
+
# eliminating circular object references.
|
101
|
+
#
|
102
|
+
# @see flatten_property
|
103
|
+
def self.flatten_property!(collection, property, property_key)
|
104
|
+
collection.each do |i|
|
105
|
+
i[property].map! {|p| p[property_key]} if i[property]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Creates a map from +primary_key+ values to flattened +property+ values
|
110
|
+
# for each item in +collection+.
|
111
|
+
#
|
112
|
+
# For checking cross-referenced property values in automated tests,
|
113
|
+
# first process +collection+ using flatten_property to avoid voluminous
|
114
|
+
# output due to the infinite recursion between cross-referenced objects.
|
115
|
+
#
|
116
|
+
# @param collection [Array<Hash>] objects for which to flatten the
|
117
|
+
# +property+ collection
|
118
|
+
# @param primary_key [String] primary key for objects in +collection+; the
|
119
|
+
# corresponding value should be a String
|
120
|
+
# @param property [String] property of objects in +collection+
|
121
|
+
# corresponding to a list of (possibly cross-referenced) Hash objects
|
122
|
+
# @param property_key [String] primary key of cross-referenced objects; the
|
123
|
+
# corresponding value should be a String
|
124
|
+
# @return [Hash<String, Array<String>>] +primary_key+ values =>
|
125
|
+
# flattened +property+ values
|
126
|
+
# @see flatten_property
|
127
|
+
def self.property_map(collection, primary_key, property, property_key)
|
128
|
+
collection.map do |i|
|
129
|
+
[i[primary_key], i[property].map {|p| p[property_key]}] if i[property]
|
130
|
+
end.compact.to_h
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Implements CrossReferencer operations.
|
135
|
+
class CrossReferencerImpl
|
136
|
+
attr_reader :site_data
|
137
|
+
|
138
|
+
def initialize(site_data)
|
139
|
+
@site_data = site_data
|
140
|
+
@team = @site_data['team'].map {|i| [i['name'], i]}.to_h
|
141
|
+
end
|
142
|
+
|
143
|
+
# Cross-references geographic locations with team members.
|
144
|
+
#
|
145
|
+
# The resulting site.data['locations'] collection will be an Array of
|
146
|
+
# [location code, Array<Hash> of team members].
|
147
|
+
def xref_locations_and_team_members
|
148
|
+
locations = CrossReferencer.create_index(@site_data['team'], 'location')
|
149
|
+
@site_data['locations'] = locations.to_a.sort! unless locations.empty?
|
150
|
+
end
|
151
|
+
|
152
|
+
# Cross-references projects with team members. Replaces string-based
|
153
|
+
# site_data['projects']['team'] values with team member hashes.
|
154
|
+
def xref_projects_and_team_members
|
155
|
+
projects = @site_data['projects']
|
156
|
+
projects.each {|p| p['team'] = p['team'].split(/, ?/) if p['team']}
|
157
|
+
CrossReferencer.create_xrefs projects, 'team', @team, 'projects'
|
158
|
+
end
|
159
|
+
|
160
|
+
# Cross-references groups with team members.
|
161
|
+
#
|
162
|
+
# @param groups_name [String] site.data key identifying the group
|
163
|
+
# collection, e.g. 'working_groups'
|
164
|
+
# @param member_type_list_names [Array<String>] names of the properties
|
165
|
+
# identifying lists of members, e.g. ['leads', 'members']
|
166
|
+
def xref_groups_and_team_members(groups_name, member_type_list_names)
|
167
|
+
member_type_list_names.each do |member_type|
|
168
|
+
CrossReferencer.create_xrefs(
|
169
|
+
@site_data[groups_name], member_type, @team, groups_name)
|
170
|
+
end
|
171
|
+
@team.values.each {|i| (i[groups_name] || []).uniq! {|g| g['name']}}
|
172
|
+
end
|
173
|
+
|
174
|
+
# Cross-references snippets with team members. Also sets
|
175
|
+
# site.data['snippets_latest'] and @site_data['snippets_team_members'].
|
176
|
+
def xref_snippets_and_team_members
|
177
|
+
(@site_data['snippets'] || []).each do |timestamp, snippets|
|
178
|
+
snippets.each do |snippet|
|
179
|
+
(@team[snippet['name']]['snippets'] ||= Array.new) << snippet
|
180
|
+
end
|
181
|
+
|
182
|
+
# Since the snippets are naturally ordered in chronological order,
|
183
|
+
# the last will be the latest.
|
184
|
+
@site_data['snippets_latest'] = timestamp
|
185
|
+
end
|
186
|
+
|
187
|
+
@site_data['snippets_team_members'] = @team.values.select do |i|
|
188
|
+
i['snippets']
|
189
|
+
end unless (@site_data['snippets'] || []).empty?
|
190
|
+
end
|
191
|
+
|
192
|
+
# Cross-references skillsets with team members.
|
193
|
+
#
|
194
|
+
# @param skills [Array<String>] list of skill categories; may be
|
195
|
+
# capitalized, though the members of site.data['team'] pertaining to
|
196
|
+
# each category should be lowercased
|
197
|
+
def xref_skills_and_team_members(categories)
|
198
|
+
skills = categories.map {|category| [category, Hash.new]}.to_h
|
199
|
+
|
200
|
+
@team.values.each do |i|
|
201
|
+
skills.each do |category, xref|
|
202
|
+
(i[category.downcase] || []).each {|s| (xref[s] ||= Array.new) << i}
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
skills.delete_if {|category, skill_xref| skill_xref.empty?}
|
207
|
+
@site_data['skills'] = skills unless skills.empty?
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
data/lib/team_hub/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: team_hub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
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-
|
11
|
+
date: 2015-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hash-joiner
|
@@ -136,6 +136,7 @@ files:
|
|
136
136
|
- README.md
|
137
137
|
- lib/team_hub.rb
|
138
138
|
- lib/team_hub/canonicalizer.rb
|
139
|
+
- lib/team_hub/cross_referencer.rb
|
139
140
|
- lib/team_hub/page.rb
|
140
141
|
- lib/team_hub/private_assets.rb
|
141
142
|
- lib/team_hub/version.rb
|
@@ -164,4 +165,3 @@ signing_key:
|
|
164
165
|
specification_version: 4
|
165
166
|
summary: Components for creating a team Hub using Jekyll
|
166
167
|
test_files: []
|
167
|
-
has_rdoc:
|