Class: TagURI::URI

Inherits:
Object
  • Object
show all
Defined in:
lib/tag_uri.rb

Overview

From the RFC @see tools.ietf.org/html/rfc4151#section-2.1 The general syntax of a tag URI, in ABNF [2], is:

tagURI = "tag:" taggingEntity ":" specific [ "#" fragment ]

Where:

taggingEntity = authorityName "," date
authorityName = DNSname / emailAddress
date = year ["-" month ["-" day]]
year = 4DIGIT
month = 2DIGIT
day = 2DIGIT
DNSname = DNScomp *( "."  DNScomp ) ; see RFC 1035 [3]
DNScomp = alphaNum [*(alphaNum /"-") alphaNum]
emailAddress = 1*(alphaNum /"-"/"."/"_") "@" DNSname
alphaNum = DIGIT / ALPHA
specific = *( pchar / "/" / "?" ) ; pchar from RFC 3986 [1]
fragment = *( pchar / "/" / "?" ) ; same as RFC 3986 [1]
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
pct-encoded   = "%" HEXDIG HEXDIG
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

Constant Summary collapse

ALPHANUM =

No underscores, only alphanums.

/[0-9a-zA-Z]/
HEXDIG =

No percent encoding here for hexnums.

/[0-9a-fA-F]/
PCT_ENCODED =

This is where the percent encodings are checked, e.g. %20.

/% #{HEXDIG} #{HEXDIG}/x
SUB_DELIMS =

Magic markers here.

/[\!\$\&'\(\)\*\+,\;\=]/x
UNRESERVED =

The uncarved block.

/[\w\-\.\~]/
PCHAR =

(Possibly percent-encoded) characters

/#{UNRESERVED} | #{PCT_ENCODED} | #{SUB_DELIMS} | \: | @/x
SPECIFIC =

The thing really being tagged.

%r< (?: #{PCHAR} | / | \? )+ >x
FRAGMENT =

fragment = *( pchar / “/” / “?” ) ; same as RFC 3986 [1] Even more specific.

/(?<=\#)#{SPECIFIC}/x
DNSNAME =

Don’t get too hung up on validating it’s a domain name, just do enough.

/
  \b
  (?:
    (?=[a-z0-9-]{1,63}\.)
    (?: xn--)?
    [a-z0-9]+
    (?: -[a-z0-9]+)*
  \.)+
  [a-z]{2,63}
  \b
/x
EMAIL =

It’s not worth validating the email address beyond this either.

/(?<email_id>[^,]+)@(?<email_domain>[^,@]+)/
YYYY_MM_DD =

ISO8601 date

/\d{4}-\d\d-\d\d/
OTHER_DATES =
%r<\d{4}(?<separator>[\-\_/])\d\d[\-\_/]\d\d>
TAG_SCHEME =

One scheme to rule them all, at least in this module.

/(?<scheme>tag)\:/
ANY_SCHEME =

Be tolerant

/(?<scheme>#{ALPHANUM}+)\:/
AUTHORITY_NAME =

I have the authoritay!

/(?<authority_name>
  (?: #{DNSNAME} )
   |
  (?: #{EMAIL} )
)/x
TAG =

The whole pattern to capture a tag uri.

/\A
  #{ANY_SCHEME}
  #{AUTHORITY_NAME}
  ,
  (?<date>#{YYYY_MM_DD})
  \:
  (?<specific> #{SPECIFIC} )
  (?<fragment> #{FRAGMENT} )?
\z/x
VALIDATE_TAG =

Should be slightly faster than the other version.

/\A
  #{TAG_SCHEME}
  #{AUTHORITY_NAME}
  ,
  #{YYYY_MM_DD}
  \:
  #{SPECIFIC}
  (?: #{FRAGMENT} )?
\z/x
TIME_FORMAT =
"%Y-%m-%d"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri: nil, authority_name: nil, date: nil, specific: nil, fragment: nil, email: nil, domain: nil) ⇒ URI

Returns a new instance of URI.

Parameters:

  • uri (#to_s) (defaults to: nil)
  • authority_name (String) (defaults to: nil)
  • date (String, Time, #to_time) (defaults to: nil)
  • specific (String) (defaults to: nil)
  • fragment (String) (defaults to: nil)
  • email (String) (defaults to: nil)
  • domain (String) (defaults to: nil)


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
# File 'lib/tag_uri.rb', line 127

def initialize( uri:            nil,
                authority_name: nil,
                date:           nil,
                specific:       nil,
                fragment:       nil,
                email:          nil,
                domain:         nil
              )
  @scheme = "tag"
  if uri
    # handle TagURI::URI object
    if (uri.respond_to? :authority_name and
         uri.respond_to? :date and
         uri.respond_to? :specific)
        @authority_name = uri.authority_name
        @date = uri.date
        @specific = uri.specific
        return
    else
      uri = uri.to_s
      # handle something that looks very like a TagURI::URI object
      if ::URI.regexp.match uri and
        (md = TAG.match uri) and
        md[:scheme] == "tag"
          @authority_name = md[:authority_name]
          @date           = Time.strptime md[:date], TIME_FORMAT
          @specific       = md[:specific]
          self.fragment   = md[:fragment]
          return
      else # see what we can salvage from the uri
        extra_uri = Addressable::URI.parse uri
      end
    end
  end

  @date =
    if date
      if date.respond_to? :to_time
        date
      elsif date.to_s =~ /\A#{YYYY_MM_DD}$/
        Time.strptime date.to_s, TIME_FORMAT
      else
        fail ArgumentError, "Date should be a valid time/date/string that conforms to YYYY-MM-DD"
      end
    elsif uri and (md = /\A[^,]+,(?<date>#{YYYY_MM_DD})/.match uri )
      Time.strptime md[:date], TIME_FORMAT
    elsif extra_uri and (md = OTHER_DATES.match extra_uri.path)
      Time.strptime md[0], "%Y#{md[:separator]}%m#{md[:separator]}%d"
    else
      Time.now
    end

  @authority_name =
    if authority_name
      if (md = AUTHORITY_NAME.match authority_name)
        md[0]
      else
        fail ArgumentError, "Authority name must be a domain name or email address"
      end
    elsif email
      if (md = EMAIL.match email)
        md[0]
      else
        fail ArgumentError, "email given was invalid"
      end
    elsif domain
      if (md = DNSNAME.match domain)
        md[0]
      else
        fail ArgumentError, "domain given was invalid"
      end
    elsif extra_uri and extra_uri.normalized_authority
      extra_uri.normalized_authority
    elsif extra_uri and extra_uri.normalized_path and (md = AUTHORITY_NAME.match(extra_uri.normalized_path))
      md[:authority_name]
    elsif uri and (md = AUTHORITY_NAME.match uri)
      md[0]
    # else what?
    end


  @specific =
    if specific
      specific
    elsif extra_uri and (md = /,[\d\-]+\:(?<specific>#{SPECIFIC})/.match extra_uri.normalized_path )
      md[:specific]
    elsif uri and (md = /,[\d\-]+\:(?<specific>#{SPECIFIC})/.match uri )
      md[:specific]
    elsif extra_uri
      extra_uri.normalized_path
    end

  self.fragment =
    if fragment
      fragment
    elsif extra_uri and (md = /,[\d\-]+\:[^\#]+#(?<fragment>#{FRAGMENT})/.match extra_uri.normalized_path)
      md[:fragment]
    elsif uri and (md = /,[\d\-]+\:[^\#]+#(?<fragment>#{FRAGMENT})/.match uri )
      md[:fragment]
    elsif extra_uri
      extra_uri.normalized_fragment
    end

end

Instance Attribute Details

#authority_nameString

Returns:

  • (String)


243
244
245
# File 'lib/tag_uri.rb', line 243

def authority_name
  @authority_name
end

#dateString

Returns:

  • (String)


243
# File 'lib/tag_uri.rb', line 243

attr_accessor :authority_name, :date, :specific

#schemeObject (readonly)

Returns the value of attribute scheme.



235
236
237
# File 'lib/tag_uri.rb', line 235

def scheme
  @scheme
end

#specificString

Returns:

  • (String)


243
# File 'lib/tag_uri.rb', line 243

attr_accessor :authority_name, :date, :specific

Instance Method Details

#fragmentObject



246
247
248
# File 'lib/tag_uri.rb', line 246

def fragment
  @fragment || ""
end

#fragment=(frag) ⇒ Object



251
252
253
254
255
256
257
258
259
260
# File 'lib/tag_uri.rb', line 251

def fragment=( frag )
  @fragment = if frag
    frag.start_with?("#") ?
      frag.sub(/#/, "") :
      frag
  else
    ""
  end
  @fragment
end

#tagging_entityObject



263
264
265
# File 'lib/tag_uri.rb', line 263

def tagging_entity
  %Q!#{@authority_name},#{@date.strftime "%F"}!
end

#to_sObject



268
269
270
271
272
273
274
# File 'lib/tag_uri.rb', line 268

def to_s
  s = %Q!#{self.scheme}:#{self.tagging_entity}:#{self.specific.gsub(/\s/, "%20")}!
  unless @fragment.nil? or @fragment.empty?
    s << "##{self.fragment}"
  end
  s
end

#valid?Boolean

Returns:

  • (Boolean)


277
278
279
# File 'lib/tag_uri.rb', line 277

def valid?
  !!(VALIDATE_TAG =~ self.to_s)
end