rubysl-uri 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +0 -1
  3. data/.travis.yml +7 -0
  4. data/README.md +2 -2
  5. data/Rakefile +0 -1
  6. data/lib/rubysl/uri.rb +2 -0
  7. data/lib/rubysl/uri/uri.rb +29 -0
  8. data/lib/rubysl/uri/version.rb +5 -0
  9. data/lib/uri.rb +1 -0
  10. data/lib/uri/common.rb +613 -0
  11. data/lib/uri/ftp.rb +198 -0
  12. data/lib/uri/generic.rb +1126 -0
  13. data/lib/uri/http.rb +100 -0
  14. data/lib/uri/https.rb +20 -0
  15. data/lib/uri/ldap.rb +190 -0
  16. data/lib/uri/ldaps.rb +12 -0
  17. data/lib/uri/mailto.rb +266 -0
  18. data/rubysl-uri.gemspec +18 -18
  19. data/spec/decode_www_form_component_spec.rb +7 -0
  20. data/spec/decode_www_form_spec.rb +7 -0
  21. data/spec/encode_www_form_component_spec.rb +7 -0
  22. data/spec/encode_www_form_spec.rb +7 -0
  23. data/spec/eql_spec.rb +11 -0
  24. data/spec/equality_spec.rb +47 -0
  25. data/spec/escape/decode_spec.rb +5 -0
  26. data/spec/escape/encode_spec.rb +5 -0
  27. data/spec/escape/escape_spec.rb +5 -0
  28. data/spec/escape/unescape_spec.rb +5 -0
  29. data/spec/extract_spec.rb +85 -0
  30. data/spec/fixtures/classes.rb +11 -0
  31. data/spec/fixtures/normalization.rb +54 -0
  32. data/spec/ftp/build_spec.rb +5 -0
  33. data/spec/ftp/merge_spec.rb +5 -0
  34. data/spec/ftp/new2_spec.rb +5 -0
  35. data/spec/ftp/path_spec.rb +55 -0
  36. data/spec/ftp/set_typecode_spec.rb +5 -0
  37. data/spec/ftp/to_s_spec.rb +24 -0
  38. data/spec/ftp/typecode_spec.rb +9 -0
  39. data/spec/generic/absolute_spec.rb +9 -0
  40. data/spec/generic/build2_spec.rb +5 -0
  41. data/spec/generic/build_spec.rb +5 -0
  42. data/spec/generic/coerce_spec.rb +5 -0
  43. data/spec/generic/component_ary_spec.rb +5 -0
  44. data/spec/generic/component_spec.rb +9 -0
  45. data/spec/generic/default_port_spec.rb +9 -0
  46. data/spec/generic/eql_spec.rb +5 -0
  47. data/spec/generic/equal_value_spec.rb +5 -0
  48. data/spec/generic/fragment_spec.rb +9 -0
  49. data/spec/generic/hash_spec.rb +5 -0
  50. data/spec/generic/hierarchical_spec.rb +5 -0
  51. data/spec/generic/host_spec.rb +9 -0
  52. data/spec/generic/inspect_spec.rb +5 -0
  53. data/spec/generic/merge_spec.rb +9 -0
  54. data/spec/generic/minus_spec.rb +5 -0
  55. data/spec/generic/normalize_spec.rb +9 -0
  56. data/spec/generic/opaque_spec.rb +9 -0
  57. data/spec/generic/password_spec.rb +9 -0
  58. data/spec/generic/path_spec.rb +9 -0
  59. data/spec/generic/plus_spec.rb +5 -0
  60. data/spec/generic/port_spec.rb +9 -0
  61. data/spec/generic/query_spec.rb +9 -0
  62. data/spec/generic/registry_spec.rb +9 -0
  63. data/spec/generic/relative_spec.rb +5 -0
  64. data/spec/generic/route_from_spec.rb +5 -0
  65. data/spec/generic/route_to_spec.rb +5 -0
  66. data/spec/generic/scheme_spec.rb +9 -0
  67. data/spec/generic/select_spec.rb +5 -0
  68. data/spec/generic/set_fragment_spec.rb +5 -0
  69. data/spec/generic/set_host_spec.rb +5 -0
  70. data/spec/generic/set_opaque_spec.rb +5 -0
  71. data/spec/generic/set_password_spec.rb +5 -0
  72. data/spec/generic/set_path_spec.rb +5 -0
  73. data/spec/generic/set_port_spec.rb +5 -0
  74. data/spec/generic/set_query_spec.rb +5 -0
  75. data/spec/generic/set_registry_spec.rb +5 -0
  76. data/spec/generic/set_scheme_spec.rb +5 -0
  77. data/spec/generic/set_user_spec.rb +5 -0
  78. data/spec/generic/set_userinfo_spec.rb +5 -0
  79. data/spec/generic/to_s_spec.rb +5 -0
  80. data/spec/generic/use_registry_spec.rb +5 -0
  81. data/spec/generic/user_spec.rb +9 -0
  82. data/spec/generic/userinfo_spec.rb +9 -0
  83. data/spec/http/build_spec.rb +5 -0
  84. data/spec/http/request_uri_spec.rb +15 -0
  85. data/spec/join_spec.rb +60 -0
  86. data/spec/ldap/attributes_spec.rb +9 -0
  87. data/spec/ldap/build_spec.rb +5 -0
  88. data/spec/ldap/dn_spec.rb +9 -0
  89. data/spec/ldap/extensions_spec.rb +9 -0
  90. data/spec/ldap/filter_spec.rb +9 -0
  91. data/spec/ldap/hierarchical_spec.rb +5 -0
  92. data/spec/ldap/scope_spec.rb +9 -0
  93. data/spec/ldap/set_attributes_spec.rb +5 -0
  94. data/spec/ldap/set_dn_spec.rb +5 -0
  95. data/spec/ldap/set_extensions_spec.rb +5 -0
  96. data/spec/ldap/set_filter_spec.rb +5 -0
  97. data/spec/ldap/set_scope_spec.rb +5 -0
  98. data/spec/mailto/build_spec.rb +98 -0
  99. data/spec/mailto/headers_spec.rb +9 -0
  100. data/spec/mailto/set_headers_spec.rb +5 -0
  101. data/spec/mailto/set_to_spec.rb +5 -0
  102. data/spec/mailto/to_mailtext_spec.rb +5 -0
  103. data/spec/mailto/to_rfc822text_spec.rb +5 -0
  104. data/spec/mailto/to_s_spec.rb +5 -0
  105. data/spec/mailto/to_spec.rb +9 -0
  106. data/spec/merge_spec.rb +21 -0
  107. data/spec/normalize_spec.rb +34 -0
  108. data/spec/parse_spec.rb +248 -0
  109. data/spec/parser/escape_spec.rb +7 -0
  110. data/spec/parser/extract_spec.rb +8 -0
  111. data/spec/parser/inspect_spec.rb +7 -0
  112. data/spec/parser/join_spec.rb +8 -0
  113. data/spec/parser/make_regexp_spec.rb +7 -0
  114. data/spec/parser/parse_spec.rb +8 -0
  115. data/spec/parser/split_spec.rb +7 -0
  116. data/spec/parser/unescape_spec.rb +7 -0
  117. data/spec/plus_spec.rb +488 -0
  118. data/spec/regexp_spec.rb +17 -0
  119. data/spec/route_from_spec.rb +24 -0
  120. data/spec/route_to_spec.rb +27 -0
  121. data/spec/select_spec.rb +30 -0
  122. data/spec/set_component_spec.rb +46 -0
  123. data/spec/shared/eql.rb +17 -0
  124. data/spec/shared/extract.rb +83 -0
  125. data/spec/shared/join.rb +58 -0
  126. data/spec/shared/parse.rb +245 -0
  127. data/spec/split_spec.rb +5 -0
  128. data/spec/uri_spec.rb +30 -0
  129. data/spec/util/make_components_hash_spec.rb +5 -0
  130. metadata +297 -88
  131. data/lib/rubysl-uri.rb +0 -7
  132. data/lib/rubysl-uri/version.rb +0 -5
data/lib/uri/ftp.rb ADDED
@@ -0,0 +1,198 @@
1
+ #
2
+ # = uri/ftp.rb
3
+ #
4
+ # Author:: Akira Yamada <akira@ruby-lang.org>
5
+ # License:: You can redistribute it and/or modify it under the same term as Ruby.
6
+ # Revision:: $Id: ftp.rb 16085 2008-04-19 11:56:22Z knu $
7
+ #
8
+
9
+ require 'uri/generic'
10
+
11
+ module URI
12
+
13
+ #
14
+ # FTP URI syntax is defined by RFC1738 section 3.2.
15
+ #
16
+ class FTP < Generic
17
+ DEFAULT_PORT = 21
18
+
19
+ COMPONENT = [
20
+ :scheme,
21
+ :userinfo, :host, :port,
22
+ :path, :typecode
23
+ ].freeze
24
+ #
25
+ # Typecode is "a", "i" or "d".
26
+ #
27
+ # * "a" indicates a text file (the FTP command was ASCII)
28
+ # * "i" indicates a binary file (FTP command IMAGE)
29
+ # * "d" indicates the contents of a directory should be displayed
30
+ #
31
+ TYPECODE = ['a', 'i', 'd'].freeze
32
+ TYPECODE_PREFIX = ';type='.freeze
33
+
34
+ def self.new2(user, password, host, port, path,
35
+ typecode = nil, arg_check = true)
36
+ typecode = nil if typecode.size == 0
37
+ if typecode && !TYPECODE.include?(typecode)
38
+ raise ArgumentError,
39
+ "bad typecode is specified: #{typecode}"
40
+ end
41
+
42
+ # do escape
43
+
44
+ self.new('ftp',
45
+ [user, password],
46
+ host, port, nil,
47
+ typecode ? path + TYPECODE_PREFIX + typecode : path,
48
+ nil, nil, nil, arg_check)
49
+ end
50
+
51
+ #
52
+ # == Description
53
+ #
54
+ # Creates a new URI::FTP object from components, with syntax checking.
55
+ #
56
+ # The components accepted are +userinfo+, +host+, +port+, +path+ and
57
+ # +typecode+.
58
+ #
59
+ # The components should be provided either as an Array, or as a Hash
60
+ # with keys formed by preceding the component names with a colon.
61
+ #
62
+ # If an Array is used, the components must be passed in the order
63
+ # [userinfo, host, port, path, typecode]
64
+ #
65
+ # If the path supplied is absolute, it will be escaped in order to
66
+ # make it absolute in the URI. Examples:
67
+ #
68
+ # require 'uri'
69
+ #
70
+ # uri = URI::FTP.build(['user:password', 'ftp.example.com', nil,
71
+ # '/path/file.> zip', 'i'])
72
+ # puts uri.to_s -> ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=a
73
+ #
74
+ # uri2 = URI::FTP.build({:host => 'ftp.example.com',
75
+ # :path => 'ruby/src'})
76
+ # puts uri2.to_s -> ftp://ftp.example.com/ruby/src
77
+ #
78
+ def self.build(args)
79
+
80
+ # Fix the incoming path to be generic URL syntax
81
+ # FTP path -> URL path
82
+ # foo/bar /foo/bar
83
+ # /foo/bar /%2Ffoo/bar
84
+ #
85
+ if args.kind_of?(Array)
86
+ args[3] = '/' + args[3].sub(/^\//, '%2F')
87
+ else
88
+ args[:path] = '/' + args[:path].sub(/^\//, '%2F')
89
+ end
90
+
91
+ tmp = Util::make_components_hash(self, args)
92
+
93
+ if tmp[:typecode]
94
+ if tmp[:typecode].size == 1
95
+ tmp[:typecode] = TYPECODE_PREFIX + tmp[:typecode]
96
+ end
97
+ tmp[:path] << tmp[:typecode]
98
+ end
99
+
100
+ return super(tmp)
101
+ end
102
+
103
+ #
104
+ # == Description
105
+ #
106
+ # Creates a new URI::FTP object from generic URL components with no
107
+ # syntax checking.
108
+ #
109
+ # Unlike build(), this method does not escape the path component as
110
+ # required by RFC1738; instead it is treated as per RFC2396.
111
+ #
112
+ # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
113
+ # +opaque+, +query+ and +fragment+, in that order.
114
+ #
115
+ def initialize(*arg)
116
+ super(*arg)
117
+ @typecode = nil
118
+ tmp = @path.index(TYPECODE_PREFIX)
119
+ if tmp
120
+ typecode = @path[tmp + TYPECODE_PREFIX.size..-1]
121
+ self.set_path(@path[0..tmp - 1])
122
+
123
+ if arg[-1]
124
+ self.typecode = typecode
125
+ else
126
+ self.set_typecode(typecode)
127
+ end
128
+ end
129
+ end
130
+ attr_reader :typecode
131
+
132
+ def check_typecode(v)
133
+ if TYPECODE.include?(v)
134
+ return true
135
+ else
136
+ raise InvalidComponentError,
137
+ "bad typecode(expected #{TYPECODE.join(', ')}): #{v}"
138
+ end
139
+ end
140
+ private :check_typecode
141
+
142
+ def set_typecode(v)
143
+ @typecode = v
144
+ end
145
+ protected :set_typecode
146
+
147
+ def typecode=(typecode)
148
+ check_typecode(typecode)
149
+ set_typecode(typecode)
150
+ typecode
151
+ end
152
+
153
+ def merge(oth) # :nodoc:
154
+ tmp = super(oth)
155
+ if self != tmp
156
+ tmp.set_typecode(oth.typecode)
157
+ end
158
+
159
+ return tmp
160
+ end
161
+
162
+ # Returns the path from an FTP URI.
163
+ #
164
+ # RFC 1738 specifically states that the path for an FTP URI does not
165
+ # include the / which separates the URI path from the URI host. Example:
166
+ #
167
+ # ftp://ftp.example.com/pub/ruby
168
+ #
169
+ # The above URI indicates that the client should connect to
170
+ # ftp.example.com then cd pub/ruby from the initial login directory.
171
+ #
172
+ # If you want to cd to an absolute directory, you must include an
173
+ # escaped / (%2F) in the path. Example:
174
+ #
175
+ # ftp://ftp.example.com/%2Fpub/ruby
176
+ #
177
+ # This method will then return "/pub/ruby"
178
+ #
179
+ def path
180
+ return @path.sub(/^\//,'').sub(/^%2F/i,'/')
181
+ end
182
+
183
+ def to_s
184
+ save_path = nil
185
+ if @typecode
186
+ save_path = @path
187
+ @path = @path + TYPECODE_PREFIX + @typecode
188
+ end
189
+ str = super
190
+ if @typecode
191
+ @path = save_path
192
+ end
193
+
194
+ return str
195
+ end
196
+ end
197
+ @@schemes['FTP'] = FTP
198
+ end
@@ -0,0 +1,1126 @@
1
+ #
2
+ # = uri/generic.rb
3
+ #
4
+ # Author:: Akira Yamada <akira@ruby-lang.org>
5
+ # License:: You can redistribute it and/or modify it under the same term as Ruby.
6
+ # Revision:: $Id: generic.rb 16085 2008-04-19 11:56:22Z knu $
7
+ #
8
+
9
+ require 'uri/common'
10
+
11
+ module URI
12
+
13
+ #
14
+ # Base class for all URI classes.
15
+ # Implements generic URI syntax as per RFC 2396.
16
+ #
17
+ class Generic
18
+ include URI
19
+ include REGEXP
20
+
21
+ DEFAULT_PORT = nil
22
+
23
+ #
24
+ # Returns default port
25
+ #
26
+ def self.default_port
27
+ self::DEFAULT_PORT
28
+ end
29
+
30
+ def default_port
31
+ self.class.default_port
32
+ end
33
+
34
+ COMPONENT = [
35
+ :scheme,
36
+ :userinfo, :host, :port, :registry,
37
+ :path, :opaque,
38
+ :query,
39
+ :fragment
40
+ ].freeze
41
+
42
+ #
43
+ # Components of the URI in the order.
44
+ #
45
+ def self.component
46
+ self::COMPONENT
47
+ end
48
+
49
+ USE_REGISTRY = false
50
+
51
+ #
52
+ # DOC: FIXME!
53
+ #
54
+ def self.use_registry
55
+ self::USE_REGISTRY
56
+ end
57
+
58
+ #
59
+ # == Synopsis
60
+ #
61
+ # See #new
62
+ #
63
+ # == Description
64
+ #
65
+ # At first, tries to create a new URI::Generic instance using
66
+ # URI::Generic::build. But, if exception URI::InvalidComponentError is raised,
67
+ # then it URI::Escape.escape all URI components and tries again.
68
+ #
69
+ #
70
+ def self.build2(args)
71
+ begin
72
+ return self.build(args)
73
+ rescue InvalidComponentError
74
+ if args.kind_of?(Array)
75
+ return self.build(args.collect{|x|
76
+ if x
77
+ URI.escape(x)
78
+ else
79
+ x
80
+ end
81
+ })
82
+ elsif args.kind_of?(Hash)
83
+ tmp = {}
84
+ args.each do |key, value|
85
+ tmp[key] = if value
86
+ URI.escape(value)
87
+ else
88
+ value
89
+ end
90
+ end
91
+ return self.build(tmp)
92
+ end
93
+ end
94
+ end
95
+
96
+ #
97
+ # == Synopsis
98
+ #
99
+ # See #new
100
+ #
101
+ # == Description
102
+ #
103
+ # Creates a new URI::Generic instance from components of URI::Generic
104
+ # with check. Components are: scheme, userinfo, host, port, registry, path,
105
+ # opaque, query and fragment. You can provide arguments either by an Array or a Hash.
106
+ # See #new for hash keys to use or for order of array items.
107
+ #
108
+ def self.build(args)
109
+ if args.kind_of?(Array) &&
110
+ args.size == ::URI::Generic::COMPONENT.size
111
+ tmp = args
112
+ elsif args.kind_of?(Hash)
113
+ tmp = ::URI::Generic::COMPONENT.collect do |c|
114
+ if args.include?(c)
115
+ args[c]
116
+ else
117
+ nil
118
+ end
119
+ end
120
+ else
121
+ raise ArgumentError,
122
+ "expected Array of or Hash of components of #{self.class} (#{self.class.component.join(', ')})"
123
+ end
124
+
125
+ tmp << true
126
+ return self.new(*tmp)
127
+ end
128
+ #
129
+ # == Args
130
+ #
131
+ # +scheme+::
132
+ # Protocol scheme, i.e. 'http','ftp','mailto' and so on.
133
+ # +userinfo+::
134
+ # User name and password, i.e. 'sdmitry:bla'
135
+ # +host+::
136
+ # Server host name
137
+ # +port+::
138
+ # Server port
139
+ # +registry+::
140
+ # DOC: FIXME!
141
+ # +path+::
142
+ # Path on server
143
+ # +opaque+::
144
+ # DOC: FIXME!
145
+ # +query+::
146
+ # Query data
147
+ # +fragment+::
148
+ # A part of URI after '#' sign
149
+ # +arg_check+::
150
+ # Check arguments [false by default]
151
+ #
152
+ # == Description
153
+ #
154
+ # Creates a new URI::Generic instance from ``generic'' components without check.
155
+ #
156
+ def initialize(scheme,
157
+ userinfo, host, port, registry,
158
+ path, opaque,
159
+ query,
160
+ fragment,
161
+ arg_check = false)
162
+ @scheme = nil
163
+ @user = nil
164
+ @password = nil
165
+ @host = nil
166
+ @port = nil
167
+ @path = nil
168
+ @query = nil
169
+ @opaque = nil
170
+ @registry = nil
171
+ @fragment = nil
172
+
173
+ if arg_check
174
+ self.scheme = scheme
175
+ self.userinfo = userinfo
176
+ self.host = host
177
+ self.port = port
178
+ self.path = path
179
+ self.query = query
180
+ self.opaque = opaque
181
+ self.registry = registry
182
+ self.fragment = fragment
183
+ else
184
+ self.set_scheme(scheme)
185
+ self.set_userinfo(userinfo)
186
+ self.set_host(host)
187
+ self.set_port(port)
188
+ self.set_path(path)
189
+ self.set_query(query)
190
+ self.set_opaque(opaque)
191
+ self.set_registry(registry)
192
+ self.set_fragment(fragment)
193
+ end
194
+ if @registry && !self.class.use_registry
195
+ raise InvalidURIError,
196
+ "the scheme #{@scheme} does not accept registry part: #{@registry} (or bad hostname?)"
197
+ end
198
+
199
+ @scheme.freeze if @scheme
200
+ self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
201
+ self.set_port(self.default_port) if self.default_port && !@port
202
+ end
203
+ attr_reader :scheme
204
+ attr_reader :host
205
+ attr_reader :port
206
+ attr_reader :registry
207
+ attr_reader :path
208
+ attr_reader :query
209
+ attr_reader :opaque
210
+ attr_reader :fragment
211
+
212
+ # replace self by other URI object
213
+ def replace!(oth)
214
+ if self.class != oth.class
215
+ raise ArgumentError, "expected #{self.class} object"
216
+ end
217
+
218
+ component.each do |c|
219
+ self.__send__("#{c}=", oth.__send__(c))
220
+ end
221
+ end
222
+ private :replace!
223
+
224
+ def component
225
+ self.class.component
226
+ end
227
+
228
+ def check_scheme(v)
229
+ if v && SCHEME !~ v
230
+ raise InvalidComponentError,
231
+ "bad component(expected scheme component): #{v}"
232
+ end
233
+
234
+ return true
235
+ end
236
+ private :check_scheme
237
+
238
+ def set_scheme(v)
239
+ @scheme = v
240
+ end
241
+ protected :set_scheme
242
+
243
+ def scheme=(v)
244
+ check_scheme(v)
245
+ set_scheme(v)
246
+ v
247
+ end
248
+
249
+ def check_userinfo(user, password = nil)
250
+ if !password
251
+ user, password = split_userinfo(user)
252
+ end
253
+ check_user(user)
254
+ check_password(password, user)
255
+
256
+ return true
257
+ end
258
+ private :check_userinfo
259
+
260
+ def check_user(v)
261
+ if @registry || @opaque
262
+ raise InvalidURIError,
263
+ "can not set user with registry or opaque"
264
+ end
265
+
266
+ return v unless v
267
+
268
+ if USERINFO !~ v
269
+ raise InvalidComponentError,
270
+ "bad component(expected userinfo component or user component): #{v}"
271
+ end
272
+
273
+ return true
274
+ end
275
+ private :check_user
276
+
277
+ def check_password(v, user = @user)
278
+ if @registry || @opaque
279
+ raise InvalidURIError,
280
+ "can not set password with registry or opaque"
281
+ end
282
+ return v unless v
283
+
284
+ if !user
285
+ raise InvalidURIError,
286
+ "password component depends user component"
287
+ end
288
+
289
+ if USERINFO !~ v
290
+ raise InvalidComponentError,
291
+ "bad component(expected user component): #{v}"
292
+ end
293
+
294
+ return true
295
+ end
296
+ private :check_password
297
+
298
+ #
299
+ # Sets userinfo, argument is string like 'name:pass'
300
+ #
301
+ def userinfo=(userinfo)
302
+ if userinfo.nil?
303
+ return nil
304
+ end
305
+ check_userinfo(*userinfo)
306
+ set_userinfo(*userinfo)
307
+ # returns userinfo
308
+ end
309
+
310
+ def user=(user)
311
+ check_user(user)
312
+ set_user(user)
313
+ # returns user
314
+ end
315
+
316
+ def password=(password)
317
+ check_password(password)
318
+ set_password(password)
319
+ # returns password
320
+ end
321
+
322
+ def set_userinfo(user, password = nil)
323
+ unless password
324
+ user, password = split_userinfo(user)
325
+ end
326
+ @user = user
327
+ @password = password if password
328
+
329
+ [@user, @password]
330
+ end
331
+ protected :set_userinfo
332
+
333
+ def set_user(v)
334
+ set_userinfo(v, @password)
335
+ v
336
+ end
337
+ protected :set_user
338
+
339
+ def set_password(v)
340
+ @password = v
341
+ # returns v
342
+ end
343
+ protected :set_password
344
+
345
+ def split_userinfo(ui)
346
+ return nil, nil unless ui
347
+ user, password = ui.split(/:/, 2)
348
+
349
+ return user, password
350
+ end
351
+ private :split_userinfo
352
+
353
+ def escape_userpass(v)
354
+ v = URI.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
355
+ end
356
+ private :escape_userpass
357
+
358
+ def userinfo
359
+ if @user.nil?
360
+ nil
361
+ elsif @password.nil?
362
+ @user
363
+ else
364
+ @user + ':' + @password
365
+ end
366
+ end
367
+
368
+ def user
369
+ @user
370
+ end
371
+
372
+ def password
373
+ @password
374
+ end
375
+
376
+ def check_host(v)
377
+ return v unless v
378
+
379
+ if @registry || @opaque
380
+ raise InvalidURIError,
381
+ "can not set host with registry or opaque"
382
+ elsif HOST !~ v
383
+ raise InvalidComponentError,
384
+ "bad component(expected host component): #{v}"
385
+ end
386
+
387
+ return true
388
+ end
389
+ private :check_host
390
+
391
+ def set_host(v)
392
+ @host = v
393
+ end
394
+ protected :set_host
395
+
396
+ def host=(v)
397
+ check_host(v)
398
+ set_host(v)
399
+ v
400
+ end
401
+
402
+ def check_port(v)
403
+ return v unless v
404
+
405
+ if @registry || @opaque
406
+ raise InvalidURIError,
407
+ "can not set port with registry or opaque"
408
+ elsif !v.kind_of?(Fixnum) && PORT !~ v
409
+ raise InvalidComponentError,
410
+ "bad component(expected port component): #{v}"
411
+ end
412
+
413
+ return true
414
+ end
415
+ private :check_port
416
+
417
+ def set_port(v)
418
+ unless !v || v.kind_of?(Fixnum)
419
+ if v.empty?
420
+ v = nil
421
+ else
422
+ v = v.to_i
423
+ end
424
+ end
425
+ @port = v
426
+ end
427
+ protected :set_port
428
+
429
+ def port=(v)
430
+ check_port(v)
431
+ set_port(v)
432
+ port
433
+ end
434
+
435
+ def check_registry(v)
436
+ return v unless v
437
+
438
+ # raise if both server and registry are not nil, because:
439
+ # authority = server | reg_name
440
+ # server = [ [ userinfo "@" ] hostport ]
441
+ if @host || @port || @user # userinfo = @user + ':' + @password
442
+ raise InvalidURIError,
443
+ "can not set registry with host, port, or userinfo"
444
+ elsif v && REGISTRY !~ v
445
+ raise InvalidComponentError,
446
+ "bad component(expected registry component): #{v}"
447
+ end
448
+
449
+ return true
450
+ end
451
+ private :check_registry
452
+
453
+ def set_registry(v)
454
+ @registry = v
455
+ end
456
+ protected :set_registry
457
+
458
+ def registry=(v)
459
+ check_registry(v)
460
+ set_registry(v)
461
+ v
462
+ end
463
+
464
+ def check_path(v)
465
+ # raise if both hier and opaque are not nil, because:
466
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
467
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
468
+ if v && @opaque
469
+ raise InvalidURIError,
470
+ "path conflicts with opaque"
471
+ end
472
+
473
+ if @scheme
474
+ if v && v != '' && ABS_PATH !~ v
475
+ raise InvalidComponentError,
476
+ "bad component(expected absolute path component): #{v}"
477
+ end
478
+ else
479
+ if v && v != '' && ABS_PATH !~ v && REL_PATH !~ v
480
+ raise InvalidComponentError,
481
+ "bad component(expected relative path component): #{v}"
482
+ end
483
+ end
484
+
485
+ return true
486
+ end
487
+ private :check_path
488
+
489
+ def set_path(v)
490
+ @path = v
491
+ end
492
+ protected :set_path
493
+
494
+ def path=(v)
495
+ check_path(v)
496
+ set_path(v)
497
+ v
498
+ end
499
+
500
+ def check_query(v)
501
+ return v unless v
502
+
503
+ # raise if both hier and opaque are not nil, because:
504
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
505
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
506
+ if @opaque
507
+ raise InvalidURIError,
508
+ "query conflicts with opaque"
509
+ end
510
+
511
+ if v && v != '' && QUERY !~ v
512
+ raise InvalidComponentError,
513
+ "bad component(expected query component): #{v}"
514
+ end
515
+
516
+ return true
517
+ end
518
+ private :check_query
519
+
520
+ def set_query(v)
521
+ @query = v
522
+ end
523
+ protected :set_query
524
+
525
+ def query=(v)
526
+ check_query(v)
527
+ set_query(v)
528
+ v
529
+ end
530
+
531
+ def check_opaque(v)
532
+ return v unless v
533
+
534
+ # raise if both hier and opaque are not nil, because:
535
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
536
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
537
+ if @host || @port || @user || @path # userinfo = @user + ':' + @password
538
+ raise InvalidURIError,
539
+ "can not set opaque with host, port, userinfo or path"
540
+ elsif v && OPAQUE !~ v
541
+ raise InvalidComponentError,
542
+ "bad component(expected opaque component): #{v}"
543
+ end
544
+
545
+ return true
546
+ end
547
+ private :check_opaque
548
+
549
+ def set_opaque(v)
550
+ @opaque = v
551
+ end
552
+ protected :set_opaque
553
+
554
+ def opaque=(v)
555
+ check_opaque(v)
556
+ set_opaque(v)
557
+ v
558
+ end
559
+
560
+ def check_fragment(v)
561
+ return v unless v
562
+
563
+ if v && v != '' && FRAGMENT !~ v
564
+ raise InvalidComponentError,
565
+ "bad component(expected fragment component): #{v}"
566
+ end
567
+
568
+ return true
569
+ end
570
+ private :check_fragment
571
+
572
+ def set_fragment(v)
573
+ @fragment = v
574
+ end
575
+ protected :set_fragment
576
+
577
+ def fragment=(v)
578
+ check_fragment(v)
579
+ set_fragment(v)
580
+ v
581
+ end
582
+
583
+ #
584
+ # Checks if URI has a path
585
+ #
586
+ def hierarchical?
587
+ if @path
588
+ true
589
+ else
590
+ false
591
+ end
592
+ end
593
+
594
+ #
595
+ # Checks if URI is an absolute one
596
+ #
597
+ def absolute?
598
+ if @scheme
599
+ true
600
+ else
601
+ false
602
+ end
603
+ end
604
+ alias absolute absolute?
605
+
606
+ #
607
+ # Checks if URI is relative
608
+ #
609
+ def relative?
610
+ !absolute?
611
+ end
612
+
613
+ def split_path(path)
614
+ path.split(%r{/+}, -1)
615
+ end
616
+ private :split_path
617
+
618
+ def merge_path(base, rel)
619
+
620
+ # RFC2396, Section 5.2, 5)
621
+ # RFC2396, Section 5.2, 6)
622
+ base_path = split_path(base)
623
+ rel_path = split_path(rel)
624
+
625
+ # RFC2396, Section 5.2, 6), a)
626
+ base_path << '' if base_path.last == '..'
627
+ while i = base_path.index('..')
628
+ base_path.slice!(i - 1, 2)
629
+ end
630
+
631
+ if (first = rel_path.first) and first.empty?
632
+ base_path.clear
633
+ rel_path.shift
634
+ end
635
+
636
+ # RFC2396, Section 5.2, 6), c)
637
+ # RFC2396, Section 5.2, 6), d)
638
+ rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
639
+ rel_path.delete('.')
640
+
641
+ # RFC2396, Section 5.2, 6), e)
642
+ tmp = []
643
+ rel_path.each do |x|
644
+ if x == '..' &&
645
+ !(tmp.empty? || tmp.last == '..')
646
+ tmp.pop
647
+ else
648
+ tmp << x
649
+ end
650
+ end
651
+
652
+ add_trailer_slash = !tmp.empty?
653
+ if base_path.empty?
654
+ base_path = [''] # keep '/' for root directory
655
+ elsif add_trailer_slash
656
+ base_path.pop
657
+ end
658
+ while x = tmp.shift
659
+ if x == '..'
660
+ # RFC2396, Section 4
661
+ # a .. or . in an absolute path has no special meaning
662
+ base_path.pop if base_path.size > 1
663
+ else
664
+ # if x == '..'
665
+ # valid absolute (but abnormal) path "/../..."
666
+ # else
667
+ # valid absolute path
668
+ # end
669
+ base_path << x
670
+ tmp.each {|t| base_path << t}
671
+ add_trailer_slash = false
672
+ break
673
+ end
674
+ end
675
+ base_path.push('') if add_trailer_slash
676
+
677
+ return base_path.join('/')
678
+ end
679
+ private :merge_path
680
+
681
+ #
682
+ # == Args
683
+ #
684
+ # +oth+::
685
+ # URI or String
686
+ #
687
+ # == Description
688
+ #
689
+ # Destructive form of #merge
690
+ #
691
+ # == Usage
692
+ #
693
+ # require 'uri'
694
+ #
695
+ # uri = URI.parse("http://my.example.com")
696
+ # uri.merge!("/main.rbx?page=1")
697
+ # p uri
698
+ # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
699
+ #
700
+ def merge!(oth)
701
+ t = merge(oth)
702
+ if self == t
703
+ nil
704
+ else
705
+ replace!(t)
706
+ self
707
+ end
708
+ end
709
+
710
+ #
711
+ # == Args
712
+ #
713
+ # +oth+::
714
+ # URI or String
715
+ #
716
+ # == Description
717
+ #
718
+ # Merges two URI's.
719
+ #
720
+ # == Usage
721
+ #
722
+ # require 'uri'
723
+ #
724
+ # uri = URI.parse("http://my.example.com")
725
+ # p uri.merge("/main.rbx?page=1")
726
+ # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
727
+ #
728
+ def merge(oth)
729
+ begin
730
+ base, rel = merge0(oth)
731
+ rescue
732
+ raise $!.class, $!.message
733
+ end
734
+
735
+ if base == rel
736
+ return base
737
+ end
738
+
739
+ authority = rel.userinfo || rel.host || rel.port
740
+
741
+ # RFC2396, Section 5.2, 2)
742
+ if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query
743
+ base.set_fragment(rel.fragment) if rel.fragment
744
+ return base
745
+ end
746
+
747
+ base.set_query(nil)
748
+ base.set_fragment(nil)
749
+
750
+ # RFC2396, Section 5.2, 4)
751
+ if !authority
752
+ base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
753
+ else
754
+ # RFC2396, Section 5.2, 4)
755
+ base.set_path(rel.path) if rel.path
756
+ end
757
+
758
+ # RFC2396, Section 5.2, 7)
759
+ base.set_userinfo(rel.userinfo) if rel.userinfo
760
+ base.set_host(rel.host) if rel.host
761
+ base.set_port(rel.port) if rel.port
762
+ base.set_query(rel.query) if rel.query
763
+ base.set_fragment(rel.fragment) if rel.fragment
764
+
765
+ return base
766
+ end # merge
767
+ alias + merge
768
+
769
+ # return base and rel.
770
+ # you can modify `base', but can not `rel'.
771
+ def merge0(oth)
772
+ case oth
773
+ when Generic
774
+ when String
775
+ oth = URI.parse(oth)
776
+ else
777
+ raise ArgumentError,
778
+ "bad argument(expected URI object or URI string)"
779
+ end
780
+
781
+ if self.relative? && oth.relative?
782
+ raise BadURIError,
783
+ "both URI are relative"
784
+ end
785
+
786
+ if self.absolute? && oth.absolute?
787
+ #raise BadURIError,
788
+ # "both URI are absolute"
789
+ # hmm... should return oth for usability?
790
+ return oth, oth
791
+ end
792
+
793
+ if self.absolute?
794
+ return self.dup, oth
795
+ else
796
+ return oth, oth
797
+ end
798
+ end
799
+ private :merge0
800
+
801
+ def route_from_path(src, dst)
802
+ # RFC2396, Section 4.2
803
+ return '' if src == dst
804
+
805
+ src_path = split_path(src)
806
+ dst_path = split_path(dst)
807
+
808
+ # hmm... dst has abnormal absolute path,
809
+ # like "/./", "/../", "/x/../", ...
810
+ if dst_path.include?('..') ||
811
+ dst_path.include?('.')
812
+ return dst.dup
813
+ end
814
+
815
+ src_path.pop
816
+
817
+ # discard same parts
818
+ while dst_path.first == src_path.first
819
+ break if dst_path.empty?
820
+
821
+ src_path.shift
822
+ dst_path.shift
823
+ end
824
+
825
+ tmp = dst_path.join('/')
826
+
827
+ # calculate
828
+ if src_path.empty?
829
+ if tmp.empty?
830
+ return './'
831
+ elsif dst_path.first.include?(':') # (see RFC2396 Section 5)
832
+ return './' + tmp
833
+ else
834
+ return tmp
835
+ end
836
+ end
837
+
838
+ return '../' * src_path.size + tmp
839
+ end
840
+ private :route_from_path
841
+
842
+ def route_from0(oth)
843
+ case oth
844
+ when Generic
845
+ when String
846
+ oth = URI.parse(oth)
847
+ else
848
+ raise ArgumentError,
849
+ "bad argument(expected URI object or URI string)"
850
+ end
851
+
852
+ if self.relative?
853
+ raise BadURIError,
854
+ "relative URI: #{self}"
855
+ end
856
+ if oth.relative?
857
+ raise BadURIError,
858
+ "relative URI: #{oth}"
859
+ end
860
+
861
+ if self.scheme != oth.scheme
862
+ return self, self.dup
863
+ end
864
+ rel = URI::Generic.new(nil, # it is relative URI
865
+ self.userinfo, self.host, self.port,
866
+ self.registry, self.path, self.opaque,
867
+ self.query, self.fragment)
868
+
869
+ if rel.userinfo != oth.userinfo ||
870
+ rel.host.to_s.downcase != oth.host.to_s.downcase ||
871
+ rel.port != oth.port
872
+ if self.userinfo.nil? && self.host.nil?
873
+ return self, self.dup
874
+ end
875
+ rel.set_port(nil) if rel.port == oth.default_port
876
+ return rel, rel
877
+ end
878
+ rel.set_userinfo(nil)
879
+ rel.set_host(nil)
880
+ rel.set_port(nil)
881
+
882
+ if rel.path && rel.path == oth.path
883
+ rel.set_path('')
884
+ rel.set_query(nil) if rel.query == oth.query
885
+ return rel, rel
886
+ elsif rel.opaque && rel.opaque == oth.opaque
887
+ rel.set_opaque('')
888
+ rel.set_query(nil) if rel.query == oth.query
889
+ return rel, rel
890
+ end
891
+
892
+ # you can modify `rel', but can not `oth'.
893
+ return oth, rel
894
+ end
895
+ private :route_from0
896
+ #
897
+ # == Args
898
+ #
899
+ # +oth+::
900
+ # URI or String
901
+ #
902
+ # == Description
903
+ #
904
+ # Calculates relative path from oth to self
905
+ #
906
+ # == Usage
907
+ #
908
+ # require 'uri'
909
+ #
910
+ # uri = URI.parse('http://my.example.com/main.rbx?page=1')
911
+ # p uri.route_from('http://my.example.com')
912
+ # #=> #<URI::Generic:0x20218858 URL:/main.rbx?page=1>
913
+ #
914
+ def route_from(oth)
915
+ # you can modify `rel', but can not `oth'.
916
+ begin
917
+ oth, rel = route_from0(oth)
918
+ rescue
919
+ raise $!.class, $!.message
920
+ end
921
+ if oth == rel
922
+ return rel
923
+ end
924
+
925
+ rel.set_path(route_from_path(oth.path, self.path))
926
+ if rel.path == './' && self.query
927
+ # "./?foo" -> "?foo"
928
+ rel.set_path('')
929
+ end
930
+
931
+ return rel
932
+ end
933
+
934
+ alias - route_from
935
+
936
+ #
937
+ # == Args
938
+ #
939
+ # +oth+::
940
+ # URI or String
941
+ #
942
+ # == Description
943
+ #
944
+ # Calculates relative path to oth from self
945
+ #
946
+ # == Usage
947
+ #
948
+ # require 'uri'
949
+ #
950
+ # uri = URI.parse('http://my.example.com')
951
+ # p uri.route_to('http://my.example.com/main.rbx?page=1')
952
+ # #=> #<URI::Generic:0x2020c2f6 URL:/main.rbx?page=1>
953
+ #
954
+ def route_to(oth)
955
+ case oth
956
+ when Generic
957
+ when String
958
+ oth = URI.parse(oth)
959
+ else
960
+ raise ArgumentError,
961
+ "bad argument(expected URI object or URI string)"
962
+ end
963
+
964
+ oth.route_from(self)
965
+ end
966
+
967
+ #
968
+ # Returns normalized URI
969
+ #
970
+ def normalize
971
+ uri = dup
972
+ uri.normalize!
973
+ uri
974
+ end
975
+
976
+ #
977
+ # Destructive version of #normalize
978
+ #
979
+ def normalize!
980
+ if path && path == ''
981
+ set_path('/')
982
+ end
983
+ if host && host != host.downcase
984
+ set_host(self.host.downcase)
985
+ end
986
+
987
+ if scheme && scheme != scheme.downcase
988
+ set_scheme(self.scheme.downcase)
989
+ end
990
+ end
991
+
992
+ def path_query
993
+ str = @path
994
+ if @query
995
+ str += '?' + @query
996
+ end
997
+ str
998
+ end
999
+ private :path_query
1000
+
1001
+ #
1002
+ # Constructs String from URI
1003
+ #
1004
+ def to_s
1005
+ str = ''
1006
+ if @scheme
1007
+ str << @scheme
1008
+ str << ':'
1009
+ end
1010
+
1011
+ if @opaque
1012
+ str << @opaque
1013
+
1014
+ else
1015
+ if @registry
1016
+ str << @registry
1017
+ else
1018
+ if @host
1019
+ str << '//'
1020
+ end
1021
+ if self.userinfo
1022
+ str << self.userinfo
1023
+ str << '@'
1024
+ end
1025
+ if @host
1026
+ str << @host
1027
+ end
1028
+ if @port && @port != self.default_port
1029
+ str << ':'
1030
+ str << @port.to_s
1031
+ end
1032
+ end
1033
+
1034
+ str << path_query
1035
+ end
1036
+
1037
+ if @fragment
1038
+ str << '#'
1039
+ str << @fragment
1040
+ end
1041
+
1042
+ str
1043
+ end
1044
+
1045
+ #
1046
+ # Compares to URI's
1047
+ #
1048
+ def ==(oth)
1049
+ if self.class == oth.class
1050
+ self.normalize.component_ary == oth.normalize.component_ary
1051
+ else
1052
+ false
1053
+ end
1054
+ end
1055
+
1056
+ def hash
1057
+ self.component_ary.hash
1058
+ end
1059
+
1060
+ def eql?(oth)
1061
+ oth.is_a?(URI) && self.component_ary.eql?(oth.component_ary)
1062
+ end
1063
+
1064
+ =begin
1065
+
1066
+ --- URI::Generic#===(oth)
1067
+
1068
+ =end
1069
+ # def ===(oth)
1070
+ # raise NotImplementedError
1071
+ # end
1072
+
1073
+ =begin
1074
+ =end
1075
+ def component_ary
1076
+ component.collect do |x|
1077
+ self.send(x)
1078
+ end
1079
+ end
1080
+ protected :component_ary
1081
+
1082
+ # == Args
1083
+ #
1084
+ # +components+::
1085
+ # Multiple Symbol arguments defined in URI::HTTP
1086
+ #
1087
+ # == Description
1088
+ #
1089
+ # Selects specified components from URI
1090
+ #
1091
+ # == Usage
1092
+ #
1093
+ # require 'uri'
1094
+ #
1095
+ # uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx')
1096
+ # p uri.select(:userinfo, :host, :path)
1097
+ # # => ["myuser:mypass", "my.example.com", "/test.rbx"]
1098
+ #
1099
+ def select(*components)
1100
+ components.collect do |c|
1101
+ if component.include?(c)
1102
+ self.send(c)
1103
+ else
1104
+ raise ArgumentError,
1105
+ "expected of components of #{self.class} (#{self.class.component.join(', ')})"
1106
+ end
1107
+ end
1108
+ end
1109
+
1110
+ @@to_s = Kernel.instance_method(:to_s)
1111
+ def inspect
1112
+ @@to_s.bind(self).call.sub!(/>\z/) {" URL:#{self}>"}
1113
+ end
1114
+
1115
+ def coerce(oth)
1116
+ case oth
1117
+ when String
1118
+ oth = URI.parse(oth)
1119
+ else
1120
+ super
1121
+ end
1122
+
1123
+ return oth, self
1124
+ end
1125
+ end
1126
+ end