law_doc 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,452 @@
1
+ module LawDoc
2
+ # LawDoc class for representing a legal document, a pleading, motion, or
3
+ # other litigation-related document.
4
+ class Document
5
+ attr_reader :preamble, :postamble
6
+
7
+ include InitTypes
8
+
9
+ # TODO: since most of the setter-getter methods, like signers, etc, depend on there being a kase and
10
+ # on_behalf_of set, perhaps those arguments should be mandatory and set before anything else.
11
+
12
+ def initialize(title: nil, kase: nil,
13
+ sig_date: nil,
14
+ on_behalf_of: nil,
15
+ signers: nil, signer: nil,
16
+ server: nil,
17
+ service_date: nil)
18
+ @kase = kase
19
+ @title = title
20
+ @on_behalf_of = []
21
+ self.on_behalf_of = on_behalf_of if on_behalf_of
22
+ @signers = []
23
+ @signers << signer if signer
24
+ @signers += signers if signers
25
+ @sig_date = init_date(sig_date) || Date.today
26
+ @service_date = service_date || @sig_date
27
+ @server = init_person(server)
28
+ @server = @signers.first if @server.nil?
29
+ @preamble = <<~'PRE'
30
+ \makeatletter
31
+ \usepackage{longtable}
32
+ % \blkwd is meant to be the width of one block, such as a lawyers block,
33
+ % such that two side-by-side in a table take up 95% of the text width
34
+ \newlength{\blkwd}%
35
+ \setlength{\blkwd}{(\textwidth - \tabcolsep * 4) * \real{0.95} / 2}%
36
+ PRE
37
+ @postamble = <<~'POST'
38
+ \makeatother
39
+ POST
40
+ yield self if block_given?
41
+ end
42
+
43
+ def to_h
44
+ {
45
+ title: title, kase: kase, sig_date: sig_date,
46
+ signers: signers, service_date: service_date,
47
+ on_behalf_of: on_behalf_of
48
+ }
49
+ end
50
+
51
+ include Comparable
52
+
53
+ def <=>(other)
54
+ to_h <=> other.to_h
55
+ end
56
+
57
+ #######################################################################
58
+ # Attributes
59
+ #######################################################################
60
+
61
+ # Set or return title
62
+ def title(*title)
63
+ title.empty? ? @title : @title = title.first.to_s
64
+ end
65
+
66
+ # Set or return kase
67
+ def kase(*kase)
68
+ kase.empty? ? @kase : @kase = init_case(kase.first)
69
+ end
70
+
71
+ # Set or return sig_date
72
+ def sig_date(*sig_date)
73
+ if sig_date.empty?
74
+ @sig_date
75
+ else
76
+ @sig_date = init_date(sig_date.first, field: 'sig_date')
77
+ end
78
+ end
79
+
80
+ # Set or return service_date
81
+ def service_date(*service_date)
82
+ if service_date.empty?
83
+ @service_date
84
+ else
85
+ @service_date = init_date(service_date.first, field: 'service_date')
86
+ end
87
+ end
88
+
89
+ # Set or return signers
90
+ def signers(*signers)
91
+ if signers.empty?
92
+ @signers
93
+ else
94
+ signers.each do |sgnr|
95
+ signer(sgnr)
96
+ end
97
+ end
98
+ end
99
+
100
+ # Set a single signer
101
+ def signer(person = nil)
102
+ return @signers.first if person.nil?
103
+
104
+ person = init_person(person, field: 'signers')
105
+ raise ArgumentError, 'signer must be a Person' unless person.is_a?(Person)
106
+
107
+ # Confirm that signer is an attorney for the party submitting the document before adding to signers.
108
+ signer_parties = on_behalf_of
109
+ if signer_parties.empty?
110
+ raise ArgumentError, "must set 'on_behalf_of' before signer"
111
+ end
112
+
113
+ parties_lawyers = signer_parties.map(&:lawyers).flatten.uniq
114
+ unless parties_lawyers.include?(person)
115
+ raise ArgumentError, "signer #{person.name} must be a lawyer for on_behalf_of party "
116
+ end
117
+
118
+ @signers ||= []
119
+ @signers << person
120
+ @signers = @signers.compact.uniq
121
+ end
122
+
123
+ # Set or return server
124
+ def server(*server)
125
+ case server.size
126
+ when 0
127
+ @server
128
+ else
129
+ @server = init_person(server.first)
130
+ end
131
+ end
132
+
133
+ # Set party or parties on behalf of whom the document is being
134
+ # submitted.
135
+ def on_behalf_of=(role_or_parties)
136
+ if role_or_parties.is_a?(Enumerable) && role_or_parties.size == 1
137
+ role_or_parties = role_or_parties.first
138
+ end
139
+ @on_behalf_of =
140
+ case role_or_parties
141
+ when String, Party
142
+ rop_to_parties(role_or_parties)
143
+ when Array
144
+ parties = []
145
+ role_or_parties.each do |rop|
146
+ ptys = rop_to_parties(rop)
147
+ ptys.each do |pty|
148
+ unless kase.parties.include?(pty)
149
+ raise ArgumentError, "'#{pty}' is not a party to #{kase}"
150
+ end
151
+
152
+ parties << pty
153
+ end
154
+ end
155
+ parties
156
+ else
157
+ raise ArgumentError,
158
+ "cannot convert '#{role_or_parties}' of class #{role_or_parties.class} to Parties"
159
+ end
160
+ end
161
+
162
+ # Set party or parties on behalf of whom the document is being
163
+ # submitted.
164
+ def on_behalf_of(*role_or_parties)
165
+ if role_or_parties.empty?
166
+ @on_behalf_of
167
+ else
168
+ unless kase
169
+ raise UserError,
170
+ 'must set kase /before/ setting on_behalf_of'
171
+ end
172
+ self.on_behalf_of = role_or_parties
173
+ end
174
+ end
175
+
176
+ # Parties other than those on whose behalf this document is being filed.
177
+ def not_on_behalf_of
178
+ kase.parties - on_behalf_of
179
+ end
180
+
181
+ # Return all parties with the given lawyer.
182
+ def parties_with_lawyer(lwy)
183
+ parties = Set.new
184
+ kase.parties.each do |pty|
185
+ parties << pty if pty.lawyers.include?(lwy)
186
+ end
187
+ parties
188
+ end
189
+
190
+ # Return all lawyers for the given party or parties.
191
+ def lawyers_for(parties)
192
+ parties = [parties] if parties.is_a?(Party)
193
+ lawyers = Set.new
194
+ parties.each do |pty|
195
+ pty.lawyers.each do |lwy|
196
+ lawyers << lwy
197
+ end
198
+ end
199
+ lawyers.to_a.compact
200
+ end
201
+
202
+ # Return all lawyers representing the party or parties on whose behalf this
203
+ # document is being filed.
204
+ def our_lawyers
205
+ lawyers_for(on_behalf_of)
206
+ end
207
+
208
+ # Return all lawyers /not/ representing the party or parties on whose behalf
209
+ # this document is being filed.
210
+ def their_lawyers
211
+ lawyers_for(not_on_behalf_of)
212
+ end
213
+
214
+ def tex_lawdoc_macros
215
+ result = preamble
216
+ result += kase.tex_lawdoc_macros
217
+ result += tex_lawdoc_document
218
+ result += tex_lawdoc_our_role
219
+ result += tex_lawdoc_their_role
220
+ result += tex_lawdoc_signers_block
221
+ result += tex_lawdoc_signatures
222
+ result += tex_lawdoc_our_lawyers_block
223
+ result += tex_lawdoc_their_lawyers_block
224
+ result += tex_lawdoc_service_list
225
+ result += postamble
226
+ result
227
+ end
228
+
229
+ # Macro to define lawyer information block for lawyers representing the
230
+ # party on whose behalf the document is being file.
231
+ # the pre-caption area.
232
+ def tex_lawdoc_our_lawyers_block
233
+ lwys = lawyers_for(on_behalf_of)
234
+ "\\OurLawyersBlock{%\n#{tex_lawdoc_lawyers_block(lwys)}}\n"
235
+ end
236
+
237
+ # Party role of party on whose behalf document is being filed, e.g.,
238
+ # "Plaintiff", "Defendant", etc.
239
+ def tex_lawdoc_our_role
240
+ ptys = on_behalf_of
241
+ "\\OurRole{#{kase.party_roles(ptys)}}\n"
242
+ end
243
+
244
+ # Macro to define lawyer information block for lawyers NOT representing
245
+ # the party on whose behalf the document is being file. the pre-caption
246
+ # area.
247
+ def tex_lawdoc_their_lawyers_block
248
+ lwys = lawyers_for(not_on_behalf_of)
249
+ "\\TheirLawyersBlock{%\n#{tex_lawdoc_lawyers_block(lwys)}}\n"
250
+ end
251
+
252
+ # Party role of party of opposing counsel, e.g., "Plaintiff", "Defendant",
253
+ # etc.
254
+ def tex_lawdoc_their_role
255
+ ptys = not_on_behalf_of
256
+ "\\TheirRole{#{kase.party_roles(ptys)}}\n"
257
+ end
258
+
259
+ # Macro to define lawyer information block, not for the signatures, but
260
+ # where a list of the lawyers signing the document are needed, e.g., in
261
+ # the pre-caption area.
262
+ def tex_lawdoc_signers_block
263
+ "\\SignersBlock{%\n#{tex_lawdoc_lawyers_block(signers)}}\n"
264
+ end
265
+
266
+ # Macro to define lawyer information block for lawyer serving the document
267
+ # to opposing counsel. Not much needed in the era of electronic filing.
268
+ def tex_lawdoc_service_list
269
+ lwys = lawyers_for(not_on_behalf_of)
270
+ "\\ServiceList{%\n#{tex_lawdoc_lawyers_block(lwys)}}\n"
271
+ end
272
+
273
+ # Longtable that places lawyer info block for the given array of lawyers
274
+ # in a table two across.
275
+ def tex_lawdoc_lawyers_block(the_lwys)
276
+ # Form the output string
277
+ result = <<~'LTPRE'
278
+ {\setlength{\LTpre}{0pt}
279
+ \setlength{\LTpost}{0pt}
280
+ \setlength{\extrarowheight}{12pt}%
281
+ \begin{small}
282
+ \singlespace
283
+ \begin{longtable}[l]{ll}%
284
+ LTPRE
285
+ the_lwys.groups_of(2).each do |_k, lwys|
286
+ l1, l2 = lwys
287
+ bar_state = kase.court.state
288
+ result += "\\parbox[t]{\\blkwd}{\n"
289
+ result += l1.tex_block(bar_state: bar_state, full: true).to_s
290
+ result += "}&\n"
291
+ next unless l2
292
+
293
+ result += "\\parbox[t]{\\blkwd}{\n"
294
+ result += l2.tex_block(bar_state: bar_state, full: true).to_s
295
+ result += "}\\\\\n"
296
+ end
297
+ result += <<~'LTPOST'
298
+ \end{longtable}
299
+ \end{small}}
300
+ LTPOST
301
+ end
302
+
303
+ private
304
+
305
+ # Return string for lawyer's capacity in kase associated with this
306
+ # document.
307
+ def capacity(lawyer, full: false)
308
+ return '' unless lawyer.is_a?(Lawyer)
309
+
310
+ pname = kase.lawyer_parties_names(lawyer)
311
+ prole = kase.lawyer_roles(lawyer)
312
+ if full
313
+ "Attorney for #{prole}, #{pname}"
314
+ else
315
+ "Attorney for #{prole}"
316
+ end
317
+ end
318
+
319
+ # Translate a string or Party to an array of Party's, using matching on the
320
+ # role for a string.
321
+ def rop_to_parties(rop)
322
+ case rop
323
+ when String
324
+ kase.parties_matching_role(rop)
325
+ when Party
326
+ unless kase.parties.include?(rop)
327
+ raise ArgumentError, "'#{rop}' is not a party to #{kase}"
328
+ end
329
+
330
+ [rop]
331
+ else
332
+ raise ArgumentError, "cannot convert '#{rop}' to a Party"
333
+ end
334
+ end
335
+
336
+ def tex_lawdoc_document
337
+ result = <<~MACS
338
+ \\DocTitle{#{title.tq}}
339
+ \\SignerName{#{signer.name.tq}}
340
+ \\SignerAddress{#{signer.address.tex_block}}
341
+ \\SignerBarState{#{kase.court.state.tq}}
342
+ \\SignerBarNumber{#{signer.bar_numbers[kase.court.state]}}
343
+ \\SignerPhone{#{signer.phone.to_s.tq}}
344
+ \\SignerFax{#{signer.fax.to_s.tq}}
345
+ \\SignerEmail{#{signer.email.to_s.tq}}
346
+ \\SignerSig{#{signer.sig}}
347
+ \\SignerESig{#{signer.esig.tq}}
348
+ \\SigDate{#{sig_date.eng}}
349
+ MACS
350
+ if server
351
+ result += <<~MACS
352
+ \\ServerName{#{server.name.tq}}
353
+ \\ServerSig{#{server.sig}}
354
+ \\ServerESig{#{server.esig.tq}}
355
+ \\ServiceDate{#{service_date.eng}}
356
+ MACS
357
+ end
358
+ result
359
+ end
360
+
361
+ # Longtable that places lawyer signature block for the given array of
362
+ # lawyers in a table two across.
363
+ def tex_lawdoc_signatures
364
+ state = kase.court.state
365
+ if signers.size == 1
366
+ l1 = signers.first
367
+ cap = capacity(l1, full: true)
368
+ full_signature = "#{l1.tex_sigblock(capacity: cap, sig: l1.sig, bar_state: state, full: true)}\n"
369
+ full_esignature = "#{l1.tex_sigblock(capacity: cap, sig: l1.esig, bar_state: state, full: true)}\n"
370
+ full_blank_signature = "#{l1.tex_sigblock(capacity: cap, sig: '', bar_state: state, full: true)}\n"
371
+ signature = "#{l1.tex_sigblock(capacity: cap, sig: l1.sig, bar_state: state)}\n"
372
+ esignature = "#{l1.tex_sigblock(capacity: cap, sig: l1.esig, bar_state: state)}\n"
373
+ blank_signature = "#{l1.tex_sigblock(capacity: cap, sig: '', bar_state: state)}\n"
374
+ return <<~MACS
375
+ \\Signature{%
376
+ \\ifthenelse{\\boolean{sign}}%
377
+ {#{signature}}
378
+ {\\ifthenelse{\\boolean{esign}}%
379
+ {#{esignature}}
380
+ {#{blank_signature}}}}
381
+ \\FullSignature{%
382
+ \\ifthenelse{\\boolean{sign}}%
383
+ {#{full_signature}}
384
+ {\\ifthenelse{\\boolean{esign}}%
385
+ {#{full_esignature}}
386
+ {#{full_blank_signature}}}}
387
+ MACS
388
+ end
389
+
390
+ # Form the output string
391
+ lt_pre = <<~'PRESIGS'
392
+ {
393
+ \setlength{\parindent}{0pt}%
394
+ PRESIGS
395
+ lt_post = <<~'POSTSIGS'
396
+ }
397
+ POSTSIGS
398
+ # Six different body for variations, (1) full or normal, and (2) sig,
399
+ # esig, or blank signatures.
400
+ full_sig_body = ''
401
+ full_esig_body = ''
402
+ full_blanksig_body = ''
403
+ sig_body = ''
404
+ esig_body = ''
405
+ blanksig_body = ''
406
+ signers.groups_of(2).each do |_k, lwys|
407
+ l1, l2 = lwys
408
+ cap = capacity(l1)
409
+ full_cap = capacity(l1, full: true)
410
+ f = '\hfill'
411
+ # f = '&'
412
+ full_sig_body += "#{l1.tex_plainsigblock(capacity: full_cap, sig: l1.sig, bar_state: state, full: true)}#{f}\n"
413
+ full_esig_body += "#{l1.tex_plainsigblock(capacity: full_cap, sig: l1.esig, bar_state: state, full: true)}#{f}\n"
414
+ full_blanksig_body += "#{l1.tex_plainsigblock(capacity: full_cap, sig: '', bar_state: state, full: true)}#{f}\n"
415
+ sig_body += "#{l1.tex_plainsigblock(capacity: cap, sig: l1.sig, bar_state: state)}#{f}\n"
416
+ esig_body += "#{l1.tex_plainsigblock(capacity: cap, sig: l1.esig, bar_state: state)}#{f}\n"
417
+ blanksig_body += "#{l1.tex_plainsigblock(capacity: cap, sig: '', bar_state: state)}#{f}\n"
418
+ next unless l2
419
+
420
+ cap = capacity(l2)
421
+ f = "\n\n\\vspace{14pt}\n"
422
+ full_cap = capacity(l2, full: true)
423
+ full_sig_body += "#{l2.tex_plainsigblock(capacity: full_cap, sig: l2.sig, bar_state: state, full: true)}#{f}"
424
+ full_esig_body += "#{l2.tex_plainsigblock(capacity: full_cap, sig: l2.esig, bar_state: state, full: true)}#{f}"
425
+ full_blanksig_body += "#{l2.tex_plainsigblock(capacity: full_cap, sig: '', bar_state: state, full: true)}#{f}"
426
+ sig_body += "#{l2.tex_plainsigblock(capacity: cap, sig: l2.sig, bar_state: state)}#{f}"
427
+ esig_body += "#{l2.tex_plainsigblock(capacity: cap, sig: l2.esig, bar_state: state)}#{f}"
428
+ blanksig_body += "#{l2.tex_plainsigblock(capacity: cap, sig: '', bar_state: state)}#{f}"
429
+ end
430
+ full_signature = lt_pre + full_sig_body + lt_post
431
+ full_esignature = lt_pre + full_esig_body + lt_post
432
+ full_blank_signature = lt_pre + full_blanksig_body + lt_post
433
+ signature = lt_pre + sig_body + lt_post
434
+ esignature = lt_pre + esig_body + lt_post
435
+ blank_signature = lt_pre + blanksig_body + lt_post
436
+ <<~MACS
437
+ \\Signature{%
438
+ \\ifthenelse{\\boolean{sign}}%
439
+ {#{signature}}
440
+ {\\ifthenelse{\\boolean{esign}}%
441
+ {#{esignature}}
442
+ {#{blank_signature}}}}
443
+ \\FullSignature{%
444
+ \\ifthenelse{\\boolean{sign}}%
445
+ {#{full_signature}}
446
+ {\\ifthenelse{\\boolean{esign}}%
447
+ {#{full_esignature}}
448
+ {#{full_blank_signature}}}}
449
+ MACS
450
+ end
451
+ end
452
+ end
@@ -0,0 +1,2 @@
1
+ class UserError < RuntimeError; end
2
+ class LogicError < RuntimeError; end
@@ -0,0 +1,182 @@
1
+ module LawDoc
2
+ module InitTypes
3
+ def init_date(dt, field: 'date')
4
+ case dt
5
+ when Date
6
+ dt
7
+ when String
8
+ Date.parse(dt)
9
+ when NilClass
10
+ nil
11
+ else
12
+ msg = "cannot initialize #{field} with #{dt.class.name}"
13
+ raise ArgumentError, msg
14
+ end
15
+ end
16
+
17
+ def init_address(addr, field: 'address')
18
+ case addr
19
+ when Address
20
+ addr
21
+ when Hash
22
+ if addr.empty?
23
+ nil
24
+ else
25
+ Address.new(**addr)
26
+ end
27
+ when NilClass
28
+ nil
29
+ else
30
+ msg = "cannot initialize #{field} with #{addr.class.name}"
31
+ raise ArgumentError, msg
32
+ end
33
+ end
34
+
35
+ def init_phone(phn, field: 'phone')
36
+ case phn
37
+ when Phone
38
+ phn
39
+ when String
40
+ Phone.new(phn)
41
+ when Hash
42
+ if phn.empty?
43
+ nil
44
+ else
45
+ Phone.new(**phn)
46
+ end
47
+ when NilClass
48
+ nil
49
+ else
50
+ msg = "cannot initialize #{field} with #{phn.class.name}"
51
+ raise ArgumentError, msg
52
+ end
53
+ end
54
+
55
+ def init_person(psn, field: 'person')
56
+ case psn
57
+ when Lawyer
58
+ psn
59
+ when Person
60
+ psn
61
+ when Hash
62
+ return nil if psn.empty?
63
+
64
+ if psn.symbolize_keys.keys.include?(:bar_numbers)
65
+ Lawyer.new(**psn)
66
+ else
67
+ Person.new(**psn)
68
+ end
69
+ when NilClass
70
+ nil
71
+ else
72
+ msg = "cannot initialize #{field} with #{psn.class.name}"
73
+ raise ArgumentError, msg
74
+ end
75
+ end
76
+
77
+ def init_lawyer(lwy, field: 'lawyer')
78
+ case lwy
79
+ when Lawyer
80
+ lwy
81
+ when Hash
82
+ return nil if lwy.empty?
83
+
84
+ Lawyer.new(**lwy)
85
+ when NilClass
86
+ nil
87
+ else
88
+ msg = "cannot initialize #{field} with #{lwy.class.name}"
89
+ raise ArgumentError, msg
90
+ end
91
+ end
92
+
93
+ def init_lawyers(lwys)
94
+ case lwys
95
+ when Lawyer
96
+ [lwys]
97
+ when Array
98
+ lwys.map { |l| init_lawyer(l) }.compact
99
+ when NilClass
100
+ []
101
+ else
102
+ msg = "cannot initialize lawyers with #{lwys.class.name}"
103
+ raise ArgumentError, msg
104
+ end
105
+ end
106
+
107
+ def init_judge(jdg, field: 'judge')
108
+ case jdg
109
+ when Judge
110
+ jdg
111
+ when Hash
112
+ return nil if jdg.empty?
113
+ Judge.new(**jdg)
114
+ when NilClass
115
+ nil
116
+ else
117
+ msg = "cannot initialize #{field} with #{jdg.class.name}"
118
+ raise ArgumentError, msg
119
+ end
120
+ end
121
+
122
+ def init_court(crt, field: 'court')
123
+ case crt
124
+ when Court
125
+ crt
126
+ when Hash
127
+ return nil if crt.empty?
128
+ Court.new(**crt)
129
+ when NilClass
130
+ nil
131
+ else
132
+ msg = "cannot initialize #{field} with #{crt.class.name}"
133
+ raise ArgumentError, msg
134
+ end
135
+ end
136
+
137
+ def init_party(pty, field: 'party')
138
+ case pty
139
+ when Party
140
+ pty
141
+ when Hash
142
+ return nil if pty.empty?
143
+
144
+ Party.new(**pty)
145
+ when NilClass
146
+ nil
147
+ else
148
+ msg = "cannot initialize #{field} with #{pty.class.name}"
149
+ raise ArgumentError, msg
150
+ end
151
+ end
152
+
153
+ def init_parties(ptys)
154
+ case ptys
155
+ when Party, Hash
156
+ ptys.map { |p| init_party(p) }.compact
157
+ when Array
158
+ ptys.map { |p| init_party(p) }.compact
159
+ when NilClass
160
+ []
161
+ else
162
+ msg = "cannot initialize parties with #{ptys.class.name}"
163
+ raise ArgumentError, msg
164
+ end
165
+ end
166
+
167
+ def init_case(cs, field: 'case')
168
+ case cs
169
+ when Case
170
+ cs
171
+ when Hash
172
+ return nil if cs.empty?
173
+ Case.new(**cs)
174
+ when NilClass
175
+ nil
176
+ else
177
+ msg = "cannot initialize #{field} with #{cs.class.name}"
178
+ raise ArgumentError, msg
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,28 @@
1
+ module LawDoc
2
+ class Judge < Person
3
+ attr_reader :title
4
+ attr_accessor :initials
5
+
6
+ def initialize(initials: nil,
7
+ title: 'Judge',
8
+ **other)
9
+ other[:hon] = 'Hon.' if other[:hon].blank?
10
+ super(**other)
11
+ @initials =
12
+ if initials
13
+ initials
14
+ else
15
+ self.initials = +''
16
+ self.initials += first[0] if first
17
+ self.initials += middle[0] if middle
18
+ self.initials += last[0] if last
19
+ end
20
+ @title = title
21
+ end
22
+
23
+ def to_h
24
+ hh = super
25
+ hh.merge(initials: initials, title: title)
26
+ end
27
+ end
28
+ end