law_doc 0.1.15

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.
@@ -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