File: refxml.rb

package info (click to toggle)
ruby-kramdown-rfc2629 1.7.29-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 524 kB
  • sloc: ruby: 3,907; makefile: 4
file content (234 lines) | stat: -rw-r--r-- 7,058 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
require 'kramdown-rfc/erb'

module KramdownRFC

  extend Kramdown::Utils::Html

  def self.escattr(str)
    escape_html(str.to_s, :attribute)
  end

  AUTHOR_ATTRIBUTES = %w{
    initials surname fullname
    asciiInitials asciiSurname asciiFullname
    role
  }

  def self.ref_to_xml(k, v)
    vps = KramdownRFC::ParameterSet.new(v)
    erb = ERB.trim_new <<-REFERB, '-'
<reference anchor="<%= escattr(k) %>" <%=
        vps.attrstf("target", "quoteTitle=quote-title=quotetitle=qt") %>>
  <front>
    <%= vps.ele("title") -%>

<% vps.arr("author", true, true) do |au|
   aups = authorps_from_hash(au)
 -%>
    <author <%=aups.attrs(*AUTHOR_ATTRIBUTES)%>>
      <%= aups.ele("organization=org", aups.attr("abbrev=orgabbrev"), "") %>
    </author>
<%   aups.warn_if_leftovers  -%>
<% end -%>
    <date <%= dateattrs(vps[:date]) %>/>
  </front>
<% vps.arr("seriesinfo", false) do |k, v| -%>
  <seriesInfo name="<%=escattr(k)%>" value="<%=escattr(v)%>"/>
<% end -%>
<% vps.arr("format", false) do |k, v| -%>
  <format type="<%=escattr(k)%>" target="<%=escattr(v)%>"/>
<% end -%>
<%= vps.ele("annotation=ann", nil, nil, true) -%>
<%= vps.ele("refcontent=rc", nil, nil, true) -%>
</reference>
    REFERB
    ret = erb.result(binding)
    vps.warn_if_leftovers
    ret
  end

  def self.treat_multi_attribute_member(ps, an)
    value = ps.rest[an]
    if Hash === value
      value.each do |k, v|
        ps.rest[if k == ':'
                  an
                else
                  Kramdown::Element.attrmangle(k + an) ||
                  Kramdown::Element.attrmangle(k) ||
                  k
                end] = v
      end
    end
  end

  def self.initializify(s)      # XXX Jean-Pierre
    w = '\p{Lu}\p{Lo}'
    if s =~ /\A[-.#{w}]+[.]/u
      $&
    elsif s =~ /\A([#{w}])[^-]*/u
      ret = "#$1."
      while (s = $') && s =~ /\A(-[\p{L}])[^-]*/u
        ret << "#$1."
      end
      ret
    else
      warn "*** Can't initializify #{s}"
      s
    end
  end

  def self.looks_like_initial(s)
    s =~ /\A[\p{Lu}\p{Lo}]([-.][\p{Lu}\p{Lo}]?)*\z/u
  end

  def self.initials_from_parts_and_surname(aups, parts, s)
    ssz = s.size
    nonsurname = parts[0...-ssz]
    if (ns = parts[-ssz..-1]) != s
      warn "*** inconsistent surnames #{ns} and #{s}"
    end
    nonsurname.map{|x| initializify(x)}.join(" ")
  end

  def self.handle_ins(aups, ins_k, initials_k, surname_k)
    if ins = aups[ins_k]
      parts = ins.split('.').map(&:strip) # split on dots first
      if parts == []
        warn "*** an empty '#{ins_k}:' value is not useful, try leaving it out"
        return
      end
      # Coalesce H.-P.
      i = 1; while i < parts.size
        if parts[i][0] == "-"
          parts[i-1..i] = [parts[i-1] + "." + parts[i]]
        else
          i += 1
        end
      end
      # Multiple surnames in ins?
      parts[-1..-1] = parts[-1].split
      s = if surname = aups.rest[surname_k]
            surname.split
          else parts.reverse.take_while{|x| !looks_like_initial(x)}.reverse
          end
      aups.rest[initials_k] = initials_from_parts_and_surname(aups, parts, s)
      aups.rest[surname_k] = s.join(" ")
    end
  end

  def self.handle_name(aups, fn_k, initials_k, surname_k)
    if name = aups.rest[fn_k]
      names = name.split(/ *\| */, 2) # boundary for given/last name
      if names == []
        warn "*** an empty '#{fn_k}:' value is not useful, try leaving it out"
        return
      end
      if names[1]
        aups.rest[fn_k] = name = names.join(" ") # remove boundary
        if surname = aups.rest[surname_k]
          if surname != names[1]
            warn "*** inconsistent embedded surname #{names[1]} and surname #{surname}"
          end
        end
        aups.rest[surname_k] = names[1]
      end
      parts = name.split
      if parts == []
        warn "*** a blank '#{fn_k}:' value is not useful, try leaving it out"
        return
      end
      surname = aups.rest[surname_k] || parts[-1]
      s = surname.split
      aups.rest[initials_k] ||= initials_from_parts_and_surname(aups, parts, s)
      aups.rest[surname_k] = s.join(" ")
    end
  end

  def self.authorps_from_hash(au)
    aups = KramdownRFC::ParameterSet.new(au)
    if n = aups[:name]
      warn "** both name #{n} and fullname #{fn} are set on one author" if fn = aups.rest["fullname"]
      aups.rest["fullname"] = n
      usename = true
    end
    ["fullname", "ins", "initials", "surname"].each do |an|
      treat_multi_attribute_member(aups, an)
    end
    handle_ins(aups, :ins, "initials", "surname")
    handle_ins(aups, :asciiIns, "asciiInitials", "asciiSurname")
    # hack ("heuristic for") initials and surname from name
    # -- only works for people with exactly one last name and uncomplicated first names
    # -- add surname for people with more than one last name
    if usename
      handle_name(aups, "fullname", "initials", "surname")
      handle_name(aups, "asciiFullname", "asciiInitials", "asciiSurname")
    end
    aups
  end

  # The below anticipates the "postalLine" changes.
  # If a postalLine is used (abbreviated "postal" in YAML),
  # non-postalLine elements are appended as further postalLines.
  # This prepares for how "country" is expected to be handled
  # specially with the next schema update.
  # So an address is now best keyboarded as:
  #   postal:
  #     - Foo Street
  #     - 28359 Bar
  #   country: Germany

  PERSON_ERB = <<~ERB
    <<%= element_name%> <%=aups.attrs(*AUTHOR_ATTRIBUTES)%>>
      <%= aups.ele("organization=org", aups.attrs("abbrev=orgabbrev",
                                                  *[$options.v3 && "ascii=orgascii"]), "") %>
      <address>
<% postal_elements = %w{extaddr pobox street cityarea city region code sortingcode country postal}.select{|gi| aups.has(gi)}
   if postal_elements != [] -%>
        <postal>
<% if pl = postal_elements.delete("postal") -%>
          <%= aups.ele("postalLine=postal") %>
<%   postal_elements.each do |gi| -%>
          <%= aups.ele("postalLine=" << gi) %>
<%   end -%>
<% else -%>
<%   postal_elements.each do |gi| -%>
          <%= aups.ele(gi) %>
<%   end -%>
<% end -%>
        </postal>
<% end -%>
<% %w{phone facsimile email uri}.select{|gi| aups.has(gi)}.each do |gi| -%>
        <%= aups.ele(gi) %>
<% end -%>
      </address>
    </<%= element_name%>>
  ERB

  def self.person_element_from_aups(element_name, aups)
    erb = ERB.trim_new(PERSON_ERB, '-')
    erb.result(binding)
  end

  def self.dateattrs(date)
    begin
      case date
      when /\A\d\d\d\d\z/
        %{year="#{date}"}
      when Integer
        %{year="#{"%04d" % date}"}
      when String
        Date.parse("#{date}-01").strftime(%{year="%Y" month="%B"})
      when Date
        date.strftime(%{year="%Y" month="%B" day="%d"})
      when Array                  # this allows to explicitly give a string
        %{year="#{date.join(" ")}"}
      when nil
        %{year="n.d."}
      end

    rescue ArgumentError
      warn "*** Invalid date: #{date} -- use 2012, 2012-07, or 2012-07-28"
    end
  end
end