jgrouper 0.0.5 → 0.1.0
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 +7 -0
- data/.autotest +35 -0
- data/.gitignore +5 -3
- data/Gemfile +3 -0
- data/HISTORY.md +20 -0
- data/{LICENSE → LICENSE.txt} +2 -2
- data/README.rdoc +2 -65
- data/Rakefile +0 -20
- data/TODO.md +4 -13
- data/bin/jgrouper-audit-archiver +39 -0
- data/bin/jgrouper-export +39 -0
- data/jgrouper.gemspec +20 -22
- data/lib/jgrouper/audit_archiver.rb +330 -0
- data/lib/jgrouper/exporter.rb +85 -0
- data/lib/jgrouper/group.rb +18 -94
- data/lib/jgrouper/member.rb +47 -0
- data/lib/jgrouper/stem.rb +43 -96
- data/lib/jgrouper/version.rb +2 -1
- data/lib/jgrouper.rb +20 -93
- metadata +26 -108
- data/lib/jgrouper/group/test_case.rb +0 -18
- data/lib/jgrouper/group_type/test_case.rb +0 -23
- data/lib/jgrouper/group_type.rb +0 -129
- data/lib/jgrouper/stem/test_case.rb +0 -33
- data/lib/jgrouper/subject/test_case.rb +0 -23
- data/lib/jgrouper/subject.rb +0 -83
- data/test/factories.rb +0 -85
- data/test/helper.rb +0 -17
- data/test/test_group.rb +0 -60
- data/test/test_group_type.rb +0 -106
- data/test/test_stem.rb +0 -90
- data/test/test_subject.rb +0 -36
@@ -0,0 +1,330 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module JGrouper # :nodoc:
|
5
|
+
#
|
6
|
+
# = JGrouper::AuditArchiver - Archive Grouper audit log entries
|
7
|
+
#
|
8
|
+
# == USAGE
|
9
|
+
#
|
10
|
+
# require 'jgrouper'
|
11
|
+
# require 'jgrouper/audit_archiver'
|
12
|
+
#
|
13
|
+
# JGrouper::AuditArchiver.new { |archiver| archiver.archive }
|
14
|
+
#
|
15
|
+
class AuditArchiver
|
16
|
+
|
17
|
+
GROUPER_AUDIT_ENTRY = 'grouper_audit_entry'
|
18
|
+
GROUPER_AUDIT_TYPE = 'grouper_audit_type'
|
19
|
+
|
20
|
+
attr_writer :directory, :skip_columns, :verbose
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@config = {}
|
24
|
+
@conn = nil
|
25
|
+
@directory = Dir.pwd
|
26
|
+
@fh = nil
|
27
|
+
@mappings = {}
|
28
|
+
@skip_columns = []
|
29
|
+
@start, @stop = Time.at(0), Time.at(0)
|
30
|
+
@date = nil
|
31
|
+
@verbose = false
|
32
|
+
yield self if block_given?
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Archive oldest date from 'grouper_audit_entry' table to file.
|
38
|
+
#
|
39
|
+
def archive
|
40
|
+
entries = []
|
41
|
+
|
42
|
+
log 'starting ...'
|
43
|
+
|
44
|
+
connect do
|
45
|
+
mappings do
|
46
|
+
start_at = time_to_microseconds @start
|
47
|
+
stop_at = time_to_microseconds @stop
|
48
|
+
|
49
|
+
filehandle @directory, "grouper-audit-entries-#{ @date }.csv" do
|
50
|
+
log "archiving #{ @date } ..."
|
51
|
+
|
52
|
+
# TODO Extract!
|
53
|
+
# TODO Use prepared statement when I get JRuby, JDBC & Oracle to better cooperate on BigDecimal-ish data types
|
54
|
+
qry = "SELECT * FROM #{GROUPER_AUDIT_ENTRY} WHERE created_on BETWEEN #{start_at} AND #{stop_at} ORDER BY created_on"
|
55
|
+
stmt = @conn.create_statement
|
56
|
+
rs = stmt.execute_query qry
|
57
|
+
md = rs.meta_data
|
58
|
+
num_cols = md.column_count
|
59
|
+
while rs.next
|
60
|
+
entry = []
|
61
|
+
1.upto(num_cols) do |n|
|
62
|
+
k = md.column_name(n).downcase.to_sym
|
63
|
+
v = rs.get_object(n)
|
64
|
+
|
65
|
+
next if v.nil?
|
66
|
+
entry << k << v.to_s.gsub(/\n/, ', ')
|
67
|
+
end
|
68
|
+
|
69
|
+
entry = transform(entry)
|
70
|
+
entries << entry.each_slice(2).reject { |slice| skip? slice.first }.collect { |slice| "#{slice.first}=#{slice.last}" }
|
71
|
+
|
72
|
+
@fh.puts CSV.generate_line( entries.last, col_sep: "\t" )
|
73
|
+
yield entries.last if block_given?
|
74
|
+
end
|
75
|
+
rs.close
|
76
|
+
stmt.close
|
77
|
+
|
78
|
+
log "archiving #{ @date } (#{entries.size} entries) - done"
|
79
|
+
end
|
80
|
+
|
81
|
+
prune(entries.size, start_at, stop_at) unless entries.empty?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
log 'done'
|
86
|
+
|
87
|
+
entries
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
#
|
94
|
+
# Extract Grouper Hibernate configuration parameters
|
95
|
+
#
|
96
|
+
def configure
|
97
|
+
java_import edu.internet2.middleware.grouper.internal.dao.hibernate.HibernateDaoConfig
|
98
|
+
cfg = HibernateDaoConfig.new
|
99
|
+
%w( driver_class url username password autocommit ).each do |p|
|
100
|
+
k = "hibernate.connection.#{p}"
|
101
|
+
v = cfg.get_property k
|
102
|
+
|
103
|
+
if 'password' == p && File.exist?(v)
|
104
|
+
java_import edu.internet2.middleware.morphString.Morph
|
105
|
+
v = Morph.decryptIfFile(v)
|
106
|
+
end
|
107
|
+
|
108
|
+
@config[ p.to_sym ] = v
|
109
|
+
end
|
110
|
+
yield self
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Connect to Grouper database
|
115
|
+
#
|
116
|
+
def connect
|
117
|
+
configure do
|
118
|
+
|
119
|
+
java_import java.sql.DriverManager
|
120
|
+
java_import @config[:driver_class]
|
121
|
+
|
122
|
+
@conn = DriverManager.get_connection @config[:url], @config[:username], @config[:password]
|
123
|
+
@conn.auto_commit = false
|
124
|
+
@date = oldest_entry unless @date # Date of oldest entry
|
125
|
+
@start = @date.to_time # Start-of-day on @date
|
126
|
+
@stop = ( @date + 1 ).to_time - 1 # End-of-day on @date
|
127
|
+
yield self if block_given?
|
128
|
+
@conn.close
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Open filehandle for writing or raise exception if a) directory does not exist or b) file does exist.
|
135
|
+
# TODO DRY
|
136
|
+
#
|
137
|
+
def filehandle(directory, file)
|
138
|
+
raise "ERROR: directory does not exist - #{directory}" unless File.directory?(directory)
|
139
|
+
fn = File.join directory, file
|
140
|
+
raise "ERROR: file already exists - #{fn}" if File.exists?(fn)
|
141
|
+
File.open(fn, 'w') { |fh| @fh = fh ; yield self ; @fh = nil }
|
142
|
+
end
|
143
|
+
|
144
|
+
# TODO Use Logger...
|
145
|
+
# TODO DRY
|
146
|
+
def log(msg)
|
147
|
+
puts "#{ File.basename(__FILE__) } #{ Time.now.to_datetime.rfc3339 } - #{msg}" if verbose?
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Fetch audit type mappings.
|
152
|
+
#
|
153
|
+
def mappings
|
154
|
+
stmt = @conn.create_statement
|
155
|
+
rs = stmt.execute_query "SELECT * FROM #{GROUPER_AUDIT_TYPE}"
|
156
|
+
md = rs.meta_data
|
157
|
+
num_cols = md.column_count
|
158
|
+
|
159
|
+
while rs.next
|
160
|
+
tmp = {}
|
161
|
+
1.upto(num_cols) do |n|
|
162
|
+
k = md.column_name(n).downcase.to_sym
|
163
|
+
case k
|
164
|
+
when :created_on, :last_updated
|
165
|
+
v = microseconds_to_rfc3399 rs.float(n)
|
166
|
+
else
|
167
|
+
v = rs.get_object(n)
|
168
|
+
end
|
169
|
+
next if v.nil?
|
170
|
+
tmp[k] = v.nil? ? v : v.to_s
|
171
|
+
end
|
172
|
+
@mappings[ tmp[:id] ] = tmp unless tmp.empty?
|
173
|
+
end
|
174
|
+
|
175
|
+
rs.close
|
176
|
+
stmt.close
|
177
|
+
|
178
|
+
yield self
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# Convert microseconds to RFC3399 formatted timestamp.
|
183
|
+
#
|
184
|
+
def microseconds_to_rfc3399(microseconds)
|
185
|
+
Time.at( microseconds_to_seconds( microseconds.to_i ) ).to_datetime.rfc3339
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# Convert microseconds to seconds.
|
190
|
+
#
|
191
|
+
def microseconds_to_seconds(microseconds)
|
192
|
+
microseconds / 1000
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Return number of entries for time interval.
|
197
|
+
#
|
198
|
+
def num_entries(start_at, stop_at)
|
199
|
+
num_entries = 0
|
200
|
+
|
201
|
+
stmt = @conn.create_statement
|
202
|
+
rs = stmt.execute_query "SELECT COUNT(*) FROM #{GROUPER_AUDIT_ENTRY} WHERE created_on BETWEEN #{start_at} AND #{stop_at}"
|
203
|
+
while rs.next
|
204
|
+
num_entries = rs.get_object(1).to_s.to_i # TODO Ugly
|
205
|
+
break
|
206
|
+
end
|
207
|
+
rs.close
|
208
|
+
stmt.close
|
209
|
+
|
210
|
+
return num_entries
|
211
|
+
end
|
212
|
+
|
213
|
+
#
|
214
|
+
# Calculate archive date based on MIN(created_on)
|
215
|
+
#
|
216
|
+
def oldest_entry
|
217
|
+
d = Date.new
|
218
|
+
|
219
|
+
stmt = @conn.create_statement
|
220
|
+
rs = stmt.execute_query "SELECT MIN(created_on) FROM #{GROUPER_AUDIT_ENTRY}"
|
221
|
+
while rs.next
|
222
|
+
# Microseconds -> Seconds -> Time -> Date
|
223
|
+
d = Time.at( microseconds_to_seconds( rs.float(1) ) ).to_date
|
224
|
+
break
|
225
|
+
end
|
226
|
+
rs.close
|
227
|
+
stmt.close
|
228
|
+
|
229
|
+
d
|
230
|
+
end
|
231
|
+
|
232
|
+
#
|
233
|
+
# Prune entries from database.
|
234
|
+
#
|
235
|
+
def prune(expected, start_at, stop_at)
|
236
|
+
log "pruning #{ @date } (#{expected} entries) ..."
|
237
|
+
|
238
|
+
found = num_entries start_at, stop_at
|
239
|
+
if expected == found
|
240
|
+
|
241
|
+
@conn.auto_commit = false
|
242
|
+
qry = "DELETE FROM #{GROUPER_AUDIT_ENTRY} WHERE created_on BETWEEN #{start_at} AND #{stop_at}"
|
243
|
+
stmt = @conn.create_statement
|
244
|
+
rv = stmt.execute_update qry
|
245
|
+
if expected == rv
|
246
|
+
@conn.commit
|
247
|
+
log "pruning #{ @date } (#{rv} entries) - done"
|
248
|
+
end
|
249
|
+
stmt.close
|
250
|
+
unless expected == rv
|
251
|
+
raise "ERROR: not committing delete as number of deleted entries does not match expected number - #{rv} != #{expected}"
|
252
|
+
end
|
253
|
+
else
|
254
|
+
raise "ERROR: not deleting as number of found entries does not match expected number - #{found} != #{expected}"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# Omit column from export?
|
260
|
+
#
|
261
|
+
def skip?(name)
|
262
|
+
@skip_columns.include? name
|
263
|
+
end
|
264
|
+
|
265
|
+
#
|
266
|
+
# Convert time to microseconds
|
267
|
+
#
|
268
|
+
def time_to_microseconds(time)
|
269
|
+
time.to_i * 1000
|
270
|
+
end
|
271
|
+
|
272
|
+
#
|
273
|
+
# Perhaps basic data transformations to make audit entry more readable.
|
274
|
+
#
|
275
|
+
def transform(entry)
|
276
|
+
# TODO Better validation & error handling
|
277
|
+
idx = entry.index(:audit_type_id)
|
278
|
+
|
279
|
+
if idx
|
280
|
+
type = @mappings[ entry[ idx + 1 ] ]
|
281
|
+
if type && type[:audit_category] && type[:action_name]
|
282
|
+
# TODO Or just replace :audit_type_id entirely?
|
283
|
+
entry.insert idx + 2, :audit_category
|
284
|
+
entry.insert idx + 3, type[:audit_category]
|
285
|
+
entry.insert idx + 4, :action_name
|
286
|
+
entry.insert idx + 5, type[:action_name]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
entry.each_with_index do |v, idx|
|
291
|
+
if idx.even?
|
292
|
+
case v
|
293
|
+
when :created_on, :last_updated
|
294
|
+
entry[ idx + 1 ] = microseconds_to_rfc3399 entry[ idx + 1 ]
|
295
|
+
when :logged_in_member_id
|
296
|
+
entry[ idx + 1 ] = transform_member_uuid entry[ idx + 1 ]
|
297
|
+
else
|
298
|
+
label_key = "label_#{v}".to_sym
|
299
|
+
if type.include? label_key
|
300
|
+
entry[idx] = "#{v}:#{ type[label_key] }"
|
301
|
+
case type[label_key]
|
302
|
+
when 'memberId'
|
303
|
+
entry[ idx + 1 ] = transform_member_uuid entry[ idx + 1 ]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
#
|
312
|
+
# Transform Grouper member UUID into something more useful.
|
313
|
+
#
|
314
|
+
def transform_member_uuid(uuid)
|
315
|
+
m = JGrouper::Member.find uuid
|
316
|
+
return uuid if m.nil?
|
317
|
+
return "#{uuid} (source=#{ m.subject_source_id } id=#{ m.subject_id } type=#{ m.subject_type })"
|
318
|
+
end
|
319
|
+
|
320
|
+
#
|
321
|
+
# Are we verbose?
|
322
|
+
#
|
323
|
+
def verbose?
|
324
|
+
@verbose
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module JGrouper # :nodoc:
|
4
|
+
#
|
5
|
+
# = JGrouper::Exporter - Export (some of the) Groups registry to CSV
|
6
|
+
#
|
7
|
+
# == USAGE
|
8
|
+
#
|
9
|
+
# require 'jgrouper'
|
10
|
+
# require 'jgrouper/exporter'
|
11
|
+
#
|
12
|
+
# JGrouper::Exporter do |exporter|
|
13
|
+
# exporter.export 'stem:to:export'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
class Exporter
|
17
|
+
|
18
|
+
attr_writer :verbose
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@directory = Dir.pwd
|
22
|
+
@fh = nil
|
23
|
+
@verbose = false
|
24
|
+
yield self if block_given?
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Export (some of the) Groups registry to CSV.
|
30
|
+
#
|
31
|
+
def export( base = nil )
|
32
|
+
entries = []
|
33
|
+
|
34
|
+
filehandle @directory, "jgrouper-export.csv" do
|
35
|
+
stem = base.nil? ? Stem.root : Stem.find(base)
|
36
|
+
raise "ERROR: stem not found" if stem.nil?
|
37
|
+
log "exporting stem=#{stem.name} ..."
|
38
|
+
|
39
|
+
raise "NOT IMPLEMENTED - exporting child stems" if stem.stems.size > 0
|
40
|
+
|
41
|
+
groups = stem.groups do |child|
|
42
|
+
g = %w( display_extension extension name ).collect { |k| child.send(k) }
|
43
|
+
g << CSV.generate_line( child.members.collect { |m| "#{ m.subject_source_id }|#{ m.subject_type_id }|#{ m.subject_id }" } ).chomp
|
44
|
+
|
45
|
+
entries << CSV.generate_line(g)
|
46
|
+
@fh.puts entries.last
|
47
|
+
yield entries.last if block_given?
|
48
|
+
end
|
49
|
+
|
50
|
+
log "exporting stem=#{stem.name} - done"
|
51
|
+
end
|
52
|
+
|
53
|
+
entries
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
#
|
60
|
+
# Open filehandle for writing or raise exception if a) directory does not exist or b) file does exist.
|
61
|
+
# TODO DRY
|
62
|
+
#
|
63
|
+
def filehandle(directory, file)
|
64
|
+
raise "ERROR: directory does not exist - #{directory}" unless File.directory?(directory)
|
65
|
+
fn = File.join directory, file
|
66
|
+
raise "ERROR: file already exists - #{fn}" if File.exists?(fn)
|
67
|
+
File.open(fn, 'w') { |fh| @fh = fh ; yield self ; @fh = nil }
|
68
|
+
end
|
69
|
+
|
70
|
+
# TODO Use Logger...
|
71
|
+
# TODO DRY
|
72
|
+
def log(msg)
|
73
|
+
puts "#{ File.basename(__FILE__) } #{ Time.now.to_datetime.rfc3339 } - #{msg}" if verbose?
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Are we verbose?
|
78
|
+
#
|
79
|
+
def verbose?
|
80
|
+
@verbose
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
data/lib/jgrouper/group.rb
CHANGED
@@ -1,116 +1,40 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
1
3
|
module JGrouper # :nodoc:
|
4
|
+
|
5
|
+
#
|
6
|
+
# = JGrouper::Group - Grouper Group
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
#
|
10
|
+
# require 'jgrouper'
|
2
11
|
#
|
3
|
-
#
|
12
|
+
# TODO
|
4
13
|
#
|
5
14
|
class Group
|
6
15
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
@grouper_group = nil
|
16
|
+
def initialize( obj = nil )
|
17
|
+
@obj = obj
|
12
18
|
yield self if block_given?
|
13
19
|
self
|
14
20
|
end
|
15
21
|
|
16
22
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# group.add_type 'group type'
|
20
|
-
#
|
21
|
-
def add_type(name)
|
22
|
-
return true if types.include? name
|
23
|
-
t = Java::EduInternet2MiddlewareGrouper::GroupTypeFinder.find name, false
|
24
|
-
return false unless t
|
25
|
-
@grouper_group.add_type t
|
26
|
-
true
|
27
|
-
end
|
28
|
-
|
29
|
-
#
|
30
|
-
# Delete group type. Returns +true+ if type deleted or group does not have type.
|
31
|
-
#
|
32
|
-
# group.delete_type 'group type'
|
33
|
-
#
|
34
|
-
def delete_type(name)
|
35
|
-
t = Java::EduInternet2MiddlewareGrouper::GroupTypeFinder.find name, false
|
36
|
-
return false unless t
|
37
|
-
return true unless types.include? name
|
38
|
-
@grouper_group.delete_type t
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
|
-
#
|
43
|
-
# Find group by name or returns +nil+.
|
23
|
+
# For passing methods on to Grouper Group object.
|
44
24
|
#
|
45
|
-
# group = JGrouper::Group.find(name)
|
46
|
-
#
|
47
|
-
def self.find(name)
|
48
|
-
group = from_grouper GroupFinder.findByName( GrouperSession.startRootSession, name, false ) # How to handle sessions?
|
49
|
-
yield group if block_given?
|
50
|
-
group
|
51
|
-
end
|
52
|
-
|
53
|
-
#
|
54
|
-
# Generate JGrouper::Group instance from JSON by calling +JSON.parse(json_string)+
|
55
|
-
#
|
56
|
-
def self.json_create(json)
|
57
|
-
new do |group|
|
58
|
-
group.instance_variable_set :@display_extension, json['display_extension']
|
59
|
-
group.instance_variable_set :@display_name, json['display_name']
|
60
|
-
group.instance_variable_set :@extension, json['extension']
|
61
|
-
group.instance_variable_set :@name, json['name']
|
62
|
-
group.instance_variable_set :@types, json['types']
|
63
|
-
group.instance_variable_set :@uuid, json['uuid']
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
25
|
def method_missing(meth, *args, &block)
|
26
|
+
super if @obj.nil?
|
68
27
|
begin
|
69
|
-
|
70
|
-
|
71
|
-
@grouper_group.send(meth, *args)
|
28
|
+
block.call @obj.send(meth, *args) if block
|
29
|
+
@obj.send(meth, *args)
|
72
30
|
rescue NoMethodError
|
73
31
|
super
|
74
32
|
end
|
75
33
|
end
|
76
34
|
|
77
|
-
#
|
78
|
-
# Return JSON representation of JGrouper::Group instance.
|
79
|
-
#
|
80
|
-
def to_json
|
81
|
-
{
|
82
|
-
:json_class => self.class.name,
|
83
|
-
:display_extension => self.display_extension,
|
84
|
-
:display_name => self.display_name,
|
85
|
-
:extension => self.extension,
|
86
|
-
:name => self.name,
|
87
|
-
:types => self.types,
|
88
|
-
:uuid => self.uuid
|
89
|
-
}.to_json
|
90
|
-
end
|
91
|
-
|
92
|
-
#
|
93
|
-
# Return String representation of JGrouper::Group instance.
|
94
|
-
#
|
95
35
|
def to_s
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
def self.from_grouper(grouper)
|
103
|
-
return nil if grouper.nil?
|
104
|
-
new do |group|
|
105
|
-
group.instance_variable_set :@grouper_group, grouper
|
106
|
-
group.instance_variable_set :@display_extension, grouper.getDisplayExtension
|
107
|
-
group.instance_variable_set :@display_name, grouper.getDisplayName
|
108
|
-
group.instance_variable_set :@extension, grouper.getExtension
|
109
|
-
group.instance_variable_set :@name, grouper.getName
|
110
|
-
# TODO Testing hack
|
111
|
-
group.instance_variable_set :@types, grouper.getTypes.collect { |t| t.kind_of?(String) ? t : t.getName }
|
112
|
-
group.instance_variable_set :@uuid, grouper.getUuid
|
113
|
-
end
|
36
|
+
return nil if @obj.nil?
|
37
|
+
CSV.generate_line %w( name display_name uuid ).collect { |k| "#{k}=#{ self.send(k) }" }
|
114
38
|
end
|
115
39
|
|
116
40
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module JGrouper # :nodoc:
|
2
|
+
|
3
|
+
#
|
4
|
+
# = JGrouper::Member - Grouper Member
|
5
|
+
#
|
6
|
+
# == Usage
|
7
|
+
#
|
8
|
+
# require 'jgrouper'
|
9
|
+
#
|
10
|
+
# m = JGrouper::Member.find uuid
|
11
|
+
#
|
12
|
+
class Member
|
13
|
+
|
14
|
+
# TODO
|
15
|
+
def initialize( obj = nil )
|
16
|
+
@obj = obj
|
17
|
+
yield self if block_given?
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Find Grouper member by UUID. Returns +JGrouper::Member+ or +nil+.
|
23
|
+
#
|
24
|
+
def self.find(uuid)
|
25
|
+
m = MemberFinder.find_by_uuid GrouperSession.start_root_session, uuid, false
|
26
|
+
return nil if m.nil?
|
27
|
+
member = self.new m
|
28
|
+
yield member if block_given?
|
29
|
+
member
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# For passing methods on to Grouper member object.
|
34
|
+
#
|
35
|
+
def method_missing(meth, *args, &block)
|
36
|
+
super if @obj.nil?
|
37
|
+
begin
|
38
|
+
block.call @obj.send(meth, *args) if block
|
39
|
+
@obj.send(meth, *args)
|
40
|
+
rescue NoMethodError
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|