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,111 @@
1
+ module LawDoc
2
+ class Lawyer < Person
3
+ attr_reader :firm, :bar_numbers
4
+
5
+ def initialize(bar_numbers: {},
6
+ firm: nil,
7
+ **other)
8
+ super(**other)
9
+ @firm = init_person(firm, field: 'firm')
10
+ @bar_numbers = {}
11
+ bar_numbers.each_pair do |st, num|
12
+ @bar_numbers[st.to_s.upcase] = num.to_s
13
+ end
14
+ end
15
+
16
+ def to_h
17
+ hh = super
18
+ hh.merge(firm: firm.to_h, bar_numbers: bar_numbers)
19
+ end
20
+
21
+ # Return a LaTeX information block for the lawyer, either abbreviated,
22
+ # when full is false, or comprehensive, when full is true. This
23
+ # information is independent of the Lawyer's role in a given case, so
24
+ # things such a bar number and capacity are not part of the description.
25
+ # Compare to tex_block below.
26
+ def tex_info(sep = ' ', full: false)
27
+ components = []
28
+ components << name.tq.to_s unless name.blank?
29
+ if full
30
+ components << "\\textsc{#{firm.name.tq}}" unless firm&.name.blank?
31
+ if address
32
+ components << address.tex_info(sep)
33
+ elsif firm
34
+ components << firm.address.tex_info(sep) if firm.address
35
+ end
36
+ components << "\\textbf{Phone:} #{phone.tq}" unless phone.blank?
37
+ components << "\\textbf{Fax:} #{fax.tq}" unless fax.blank?
38
+ components << "\\textbf{Email:} \\texttt{#{email.tq}}" unless email.blank?
39
+ end
40
+ components.join_nonblank(sep)
41
+ end
42
+
43
+ # Return a LaTeX information block for the lawyer in the context of his
44
+ # role in a case, either abbreviated, when full is false, or
45
+ # comprehensive, when full is true. Information about the lawyers role in
46
+ # a case is supplied by the parameters bar_state and capacity. Compare to
47
+ # tex_info above.
48
+ def tex_block(capacity: nil, bar_state: nil, full: false)
49
+ result = tex_info("\\\\\n", full: full)
50
+ unless bar_state.nil?
51
+ key = bar_state.to_s.upcase
52
+ if bar_numbers.key?(key)
53
+ # A bar number of "N/A", "NA", "n/a", etc., means the state has no bar numbers,
54
+ # and don't include it in the block.
55
+ unless bar_numbers[key] =~ %r{n/?a}i
56
+ result += "\\\\\n\\textbf{Bar:}~#{bar_numbers[key].tq}"
57
+ end
58
+ else
59
+ result += "\\\\\n\\textbf{Bar:}~Pro Hac Motion to be Filed"
60
+ end
61
+ end
62
+ result += "\\\\\n~~\\textit{#{capacity.tq}}" if capacity
63
+ result
64
+ end
65
+
66
+ # Return a \plainsigblock macro for a signature block for this person
67
+ # using the person's signature to sign if sig is either 'sign' (for a
68
+ # graphic signature) or 'esign' for a /s/-style signature. Information
69
+ # about the Lawyer's role in a case, if any, is supplied by optional
70
+ # parameters: capacity (e.g., 'Attorney for Jones'), bar_state (e.g. 'NY')
71
+ # to act as a key for which bar_number to chose. The signature block can
72
+ # contain all the Lawyer's contact information, or just name, bar number,
73
+ # and capacity, depending on whether the full parameter is true.
74
+ def tex_plainsigblock(sig: nil, bar_state: nil, capacity: nil, full: false)
75
+ # label_part = firm ? "\\textsc{#{firm.name.tq}}" : ''
76
+ # by_part = firm ? 'By:~' : ''
77
+ below_part = tex_block(capacity: capacity, bar_state: bar_state, full: full)
78
+ "\\plainsigblock{}{}{#{sig}}{#{below_part}}"
79
+ end
80
+
81
+ # Like tex_plainsigblock, but can also take a date and returns the
82
+ # \sigblock macro, which spans the entire width of the page, so is only
83
+ # suitable for a single signer or vertically stacked signatures. Also
84
+ # adds a date and date_format parameters for placing a date to the left of
85
+ # the signature using the strftime format given in date_format. We
86
+ # formerly used the firm name, if any, to place above the signature and an
87
+ # "By" designator, but have eliminated those since a lawyer in a case
88
+ # never signs for the firm, but for himself.
89
+ def tex_sigblock(sig: nil, bar_state: nil, capacity: nil,
90
+ date: nil, date_format: nil,
91
+ full: false)
92
+ date_part =
93
+ if date
94
+ date = init_date(date) if date
95
+ if date_format
96
+ "Date: #{date.strftime(date_format).tq}"
97
+ else
98
+ "Date: #{date.iso}"
99
+ end
100
+ end
101
+ # label_part = firm ? "\\textsc{#{firm.name.tq}}" : ''
102
+ # by_part = firm ? 'By:~' : ''
103
+ below_part = tex_block(capacity: capacity, bar_state: bar_state, full: full)
104
+ if date
105
+ "\\sigblock[#{date_part}]{}{}{#{sig}}{#{below_part}}"
106
+ else
107
+ "\\sigblock{}{}{#{sig}}{#{below_part}}"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,40 @@
1
+ module LawDoc
2
+ class Party < Person
3
+ include InitTypes
4
+
5
+ attr_reader :role, :lawyers
6
+
7
+ def initialize(role: nil, lawyers: [], **other)
8
+ # Allow singular lawyer
9
+ if other.key?(:lawyer)
10
+ lawyers = other[:lawyer]
11
+ other.delete(:lawyer)
12
+ end
13
+ if other.key?(:person)
14
+ case other[:person]
15
+ when Person
16
+ person_params = other[:person].to_h
17
+ other.delete(:person)
18
+ super(**other.merge(person_params))
19
+ when Hash
20
+ person_params = other[:person]
21
+ other.delete(:person)
22
+ super(**other.merge(person_params))
23
+ end
24
+ else
25
+ super(**other)
26
+ end
27
+ @role = role
28
+ raise UserError, "party #{name} must be given a role" if role.blank?
29
+
30
+ @lawyers = init_lawyers(lawyers)
31
+ end
32
+
33
+ def to_h
34
+ result = super
35
+ result[:role] = role
36
+ result[:lawyers] = lawyers
37
+ result
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,291 @@
1
+ module LawDoc
2
+ class Person
3
+ attr_reader :sex, :hon, :first, :middle, :last,
4
+ :suffix, :title, :sig, :esig, :address,
5
+ :email, :dob
6
+
7
+ include InitTypes
8
+
9
+ def initialize(sex: 'entity', hon: nil, first: nil, last: nil,
10
+ middle: nil, suffix: nil, title: nil, sig: nil,
11
+ esig: nil, name: nil, dob: nil, address: nil,
12
+ phone: nil, fax: nil, email: nil)
13
+ @sex = sex.to_s.clean.downcase
14
+ @sex =
15
+ case @sex
16
+ when /^m/i
17
+ 'male'
18
+ when /^f/i
19
+ 'female'
20
+ else
21
+ 'entity'
22
+ end
23
+ msg = "invalid sex: '#{@sex}'"
24
+ raise ArgumentError, msg unless %w[entity male female].include?(@sex)
25
+
26
+ @hon = hon
27
+ @first = first
28
+ @middle = middle
29
+ @last = last
30
+ @suffix = suffix
31
+ @name = name
32
+ parse_name
33
+ @title = title
34
+ @sig = sig
35
+ @esig = esig || (@sex == 'entity' ? nil : "/s/ #{@name}")
36
+ @dob = init_date(dob, field: 'dob')
37
+ @address = init_address(address)
38
+ @phone = init_phone(phone)
39
+ @fax = init_phone(fax, field: 'fax')
40
+ @email = email
41
+ end
42
+
43
+ # This is here only to test alternative constructors in the ydl gem.
44
+ def self.from_hash(hsh)
45
+ new(**hsh)
46
+ end
47
+
48
+ def phone
49
+ @phone.to_s
50
+ end
51
+
52
+ def fax
53
+ @fax.to_s
54
+ end
55
+
56
+ def entity?
57
+ @sex == 'entity'
58
+ end
59
+
60
+ # Set name components from what the user has supplied.
61
+ def parse_name
62
+ if entity?
63
+ if @name
64
+ @hon = @first = @middle = @last = @suffix = nil
65
+ elsif @first || @middle || @last
66
+ @name = [@first, @middle, @last].compact.join(' ')
67
+ @hon = @first = @middle = @last = @suffix = nil
68
+ else
69
+ msg = 'entity must have name'
70
+ raise ArgumentError, msg
71
+ end
72
+ elsif @name && @last.nil?
73
+ names = @name.split
74
+ case names.size
75
+ when 5
76
+ @hon, @first, @middle, @last, @suffix = names
77
+ when 4
78
+ @first, @middle, @last, @suffix = names
79
+ when 3
80
+ @first, @middle, @last = names
81
+ when 2
82
+ @first, @last = names
83
+ else
84
+ @last = @name
85
+ end
86
+ elsif @first && @last
87
+ @name =
88
+ [@first, @middle, @last, @suffix].compact.join(' ')
89
+ else
90
+ msg = "cannot parse person's name"
91
+ raise ArgumentError, msg
92
+ end
93
+ @hon = 'Mr.' if @hon.nil? && @sex == 'male'
94
+ @hon = 'Ms.' if @hon.nil? && @sex == 'female'
95
+ end
96
+
97
+ def to_h
98
+ if entity?
99
+ {
100
+ sex: sex, name: name, address: address.to_h, phone: @phone.to_h,
101
+ fax: @fax.to_h, email: email
102
+ }
103
+ else
104
+ {
105
+ sex: sex, hon: hon, first: first, last: last, middle: middle,
106
+ address: address.to_h, phone: @phone.to_h,
107
+ fax: @fax.to_h, email: email
108
+ }
109
+ end
110
+ end
111
+
112
+ include Comparable
113
+
114
+ def <=>(other)
115
+ to_h <=> other.to_h
116
+ end
117
+
118
+ def name
119
+ if entity?
120
+ @name
121
+ else
122
+ [@first, @middle, @last, @suffix].join_nonblank(' ')
123
+ end
124
+ end
125
+
126
+ def age(on = Date.today)
127
+ on = init_date(on, field: 'on')
128
+ return nil if on < dob
129
+
130
+ birthday_this_year = Date.new(on.year, dob.month, dob.day)
131
+ if on - dob <= 365.25
132
+ # Only babies get a fractional age
133
+ ((on - dob) / 365.25).round(2)
134
+ elsif on >= birthday_this_year
135
+ on.year - dob.year
136
+ else
137
+ on.year - dob.year - 1
138
+ end
139
+ end
140
+
141
+ # Return a TeX string with components separated by sep.
142
+ def tex_info(sep = ' ')
143
+ if address
144
+ "\\textsc{#{name.tq}}" + sep + address.tex_info(sep)
145
+ else
146
+ "\\textsc{#{name.tq}}"
147
+ end
148
+ end
149
+
150
+ # Return a multi-line TeX string
151
+ def tex_block
152
+ tex_info("\\\\\n")
153
+ end
154
+
155
+ # Return an in-line TeX string
156
+ def tex_inline
157
+ if address
158
+ "\\textsc{#{name.tq}}, #{address.tex_inline}"
159
+ else
160
+ "\\textsc{#{name.tq}}"
161
+ end
162
+ end
163
+ alias to_s tex_inline
164
+
165
+ # Return a \plainsigblock macro for a signature block for this person
166
+ # using the person's signature to sign if sig is either 'sign' (for a
167
+ # graphic signature) or 'esign' for a /s/-style signature. Have signature
168
+ # by another if agent: is set to a person. If an agent is provided, set
169
+ # the capacity in which the agent signs with the agent_capacity:
170
+ # parameter.
171
+ def tex_sigblock(sig: nil, label: nil,
172
+ date: nil, date_format: nil,
173
+ below: nil,
174
+ agent: nil, capacity: nil)
175
+ date = init_date(date) if date
176
+ agent = init_person(agent, field: 'agent')
177
+ label_part =
178
+ if label
179
+ label.tq.to_s
180
+ elsif entity?
181
+ "\\textsc{#{name.tq}}"
182
+ elsif agent
183
+ name.tq
184
+ else
185
+ ''
186
+ end
187
+ by_part = agent || entity? ? 'By:~' : ''
188
+ date_part =
189
+ if date
190
+ if date_format
191
+ "Date: #{date.strftime(date_format)}"
192
+ else
193
+ "Date: #{date.iso}"
194
+ end
195
+ end
196
+ below_part =
197
+ if below
198
+ below.to_s
199
+ elsif agent
200
+ agent.name.tq + (capacity ? ", #{capacity.tq}" : '')
201
+ elsif !entity?
202
+ name.tq + (capacity ? ", #{capacity.tq}" : '')
203
+ else
204
+ ''
205
+ end
206
+ if date
207
+ "\\sigblock[#{date_part}]{#{label_part}}{#{by_part}}{#{sig}}{#{below_part}}"
208
+ else
209
+ "\\sigblock{#{label_part}}{#{by_part}}{#{sig}}{#{below_part}}"
210
+ end
211
+ end
212
+
213
+ # Return a \plainsigblock macro for a signature block for this person
214
+ # using the person's signature to sign if sig is either 'sign' (for a
215
+ # graphic signature) or 'esign' for a /s/-style signature. Have signature
216
+ # by another if agent: is set to a person. If an agent is provided, set
217
+ # the capacity in which the agent signs with the agent_capacity:
218
+ # parameter.
219
+ def tex_plainsigblock(sig: nil, label: nil, below: nil,
220
+ agent: nil, capacity: nil)
221
+ agent = init_person(agent, field: 'agent')
222
+ label_part =
223
+ if label
224
+ label.tq.to_s
225
+ elsif entity?
226
+ "\\textsc{#{name.tq}}"
227
+ elsif agent
228
+ name.tq
229
+ else
230
+ ''
231
+ end
232
+ by_part = agent || entity? ? 'By:~' : ''
233
+ below_part =
234
+ if below
235
+ below.to_s
236
+ elsif agent
237
+ agent.name.tq + (capacity ? ", #{capacity}" : '')
238
+ elsif !entity?
239
+ name.tq + (capacity ? ", #{capacity}" : '')
240
+ else
241
+ ''
242
+ end
243
+ "\\plainsigblock{#{label_part}}{#{by_part}}{#{sig}}{#{below_part}}"
244
+ end
245
+
246
+ # Sex-dependent pronouns
247
+ def he_she_it
248
+ case sex
249
+ when 'male'
250
+ 'he'
251
+ when 'female'
252
+ 'she'
253
+ else
254
+ 'it'
255
+ end
256
+ end
257
+
258
+ def him_her_it
259
+ case sex
260
+ when 'male'
261
+ 'him'
262
+ when 'female'
263
+ 'her'
264
+ else
265
+ 'it'
266
+ end
267
+ end
268
+
269
+ def his_her_its
270
+ case sex
271
+ when 'male'
272
+ 'his'
273
+ when 'female'
274
+ 'her'
275
+ else
276
+ 'its'
277
+ end
278
+ end
279
+
280
+ def his_hers_its
281
+ case sex
282
+ when 'male'
283
+ 'his'
284
+ when 'female'
285
+ 'hers'
286
+ else
287
+ 'its'
288
+ end
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,62 @@
1
+ module LawDoc
2
+ class PhoneParseError < ArgumentError; end
3
+
4
+ class Phone
5
+ cattr_accessor :default_area
6
+ self.default_area = '913'
7
+
8
+ attr_reader :area, :exchange, :station
9
+
10
+ def initialize(phn)
11
+ case phn
12
+ when String
13
+ case phn.to_s.gsub(/\D/, '')
14
+ when /^1?(\d{3})(\d{3})(\d{4})/
15
+ @area = $1
16
+ @exchange = $2
17
+ @station = $3
18
+ when /^1?(\d{3})(\d{4})/
19
+ @area = Phone.default_area
20
+ @exchange = $1
21
+ @station = $2
22
+ else
23
+ raise PhoneParseError, "cannot parse '#{phn}' as a valid phone number"
24
+ end
25
+ when Hash
26
+ if phn.empty?
27
+ nil
28
+ else
29
+ @area = phn[:area]
30
+ @exchange = phn[:exchange]
31
+ @station = phn[:station]
32
+ end
33
+ else
34
+ raise PhoneParseError, "cannot initialize phone with '#{phn.class}'"
35
+ end
36
+ end
37
+
38
+ def to_s(sep = '-')
39
+ return '' unless @area && @exchange && @station
40
+
41
+ [@area, @exchange, @station].join(sep.to_s)
42
+ end
43
+
44
+ def to_h
45
+ { area: area, exchange: exchange, station: station }
46
+ end
47
+
48
+ include Comparable
49
+
50
+ def <=>(other)
51
+ to_h <=> other.to_h
52
+ end
53
+
54
+ def tex_block
55
+ "(#{area.tq}) #{exchange.tq}-#{station.tq}"
56
+ end
57
+
58
+ def tex_line
59
+ tex_block
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ module LawDoc
2
+ VERSION = '0.1.15'.freeze
3
+ end
data/lib/law_doc.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'fat_core/all'
2
+ require 'active_support'
3
+ require 'active_support/core_ext/object/blank'
4
+
5
+ require 'law_doc/version'
6
+ require 'blankable_date'
7
+
8
+ module LawDoc
9
+ require 'law_doc/core_ext'
10
+ require 'law_doc/error'
11
+ require 'law_doc/init_types'
12
+ require 'law_doc/address'
13
+ require 'law_doc/phone'
14
+ require 'law_doc/person'
15
+ require 'law_doc/judge'
16
+ require 'law_doc/court'
17
+ require 'law_doc/lawyer'
18
+ require 'law_doc/party'
19
+ require 'law_doc/case'
20
+ require 'law_doc/document'
21
+
22
+ class << self
23
+ attr_accessor :doc
24
+ attr_accessor :kase
25
+ end
26
+
27
+ # This method returns all the LaTeX macros implied by the Ydl files found on
28
+ # the file system. It also makes the LawDoc::Document object available
29
+ # through LawDoc.doc and its kase through LawDoc.kase. Thus, an ErbTeX
30
+ # document often starts with code like the following:
31
+ #
32
+ # {:=
33
+ # require 'law_doc'
34
+ #
35
+ # LawDoc.document(ydl: true) do
36
+ # title 'Complaint Under 15 U.S.C. 78p(b)'
37
+ # kase Ydl[:cases][:coty]
38
+ # signers Ydl[:lawyers][:jhunter], Ydl[:lawyers][:tauber], Ydl[:lawyers][:ded]
39
+ # on_behalf_of 'plaintiffs'
40
+ # sig_date '2020-08-11'
41
+ # server signer
42
+ # service_date sig_date
43
+ # end
44
+ # :}
45
+
46
+ # To make access to the document and other objects easier, you can then do
47
+ # something like this:
48
+ #
49
+ # {:
50
+ # doc = LawDoc.doc
51
+ # laubies = Ydl[:persons][:laubies]
52
+ # coty = Ydl[:persons][:coty]
53
+ # :}
54
+ def self.document(ydl: false, &block)
55
+ if ydl
56
+ begin
57
+ require 'ydl'
58
+ Ydl.load
59
+ rescue LoadError
60
+ msg = <<~MSG
61
+ In order to use the ydl option, you must install the ydl gem:
62
+ try `gem install ydl`
63
+ MSG
64
+ raise UserError, msg
65
+ end
66
+ end
67
+ doc = LawDoc::Document.new
68
+ doc.instance_eval(&block)
69
+ LawDoc.doc = doc
70
+ LawDoc.kase = doc.kase
71
+ doc.tex_lawdoc_macros
72
+ end
73
+ end