url 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +26 -1
- data/Rakefile +5 -11
- data/VERSION.yml +3 -4
- data/lib/url/classer.rb +107 -0
- data/lib/url/handlers/as_json_handler.rb +9 -0
- data/lib/url/handlers/base_json_handler.rb +9 -0
- data/lib/url/handlers/net_handler.rb +59 -0
- data/lib/url/handlers/ty_handler.rb +37 -0
- data/lib/url/handlers/yajl_handler.rb +13 -0
- data/lib/url/handlers.rb +15 -78
- data/lib/url/helper_classes.rb +33 -51
- data/lib/url/response.rb +60 -0
- data/lib/url.rb +102 -19
- data/spec/classer_spec.rb +66 -0
- data/spec/handler_spec.rb +69 -0
- data/spec/json_spec.rb +13 -0
- data/spec/spec_helper.rb +1 -5
- data/spec/url_spec.rb +67 -55
- metadata +23 -12
- data/.gitignore +0 -24
data/README.rdoc
CHANGED
@@ -33,7 +33,7 @@ You can quickly and easily make requests from those urls without having to worry
|
|
33
33
|
A {URL::Response} object is returned from {URL#post}, {URL#get}, or {URL#delete}. This is basically a string with a few additional methods including:
|
34
34
|
|
35
35
|
The time the request took in seconds
|
36
|
-
The http code of the response and whether the request was successful or not (code ==
|
36
|
+
The http code of the response and whether the request was successful or not (code == 2xx).
|
37
37
|
|
38
38
|
response.time # => 1.6
|
39
39
|
response.code # => 200
|
@@ -52,10 +52,35 @@ You can make GET, POST, or DELETE requests without doing any special formatting.
|
|
52
52
|
url.params.delete(:message)
|
53
53
|
url.delete
|
54
54
|
|
55
|
+
=== JSON
|
56
|
+
|
57
|
+
If you've initialized the Yajl, JSON, or Active Service gems you can call .json on any response object.
|
58
|
+
|
59
|
+
url = URL.new('https://graph.facebook.com/37901410')
|
60
|
+
url.get.json # => => {"name"=>"Tal Atlas", "gender"=>"male", "id"=>"37901410", "last_name"=>"Atlas", "locale"=>"en_US", "link"=>"http://www.facebook.com/talatlas", "first_name"=>"Tal"}
|
61
|
+
|
62
|
+
=== Make Custom Objects
|
63
|
+
|
64
|
+
Make objects designed around specific urls
|
65
|
+
|
66
|
+
class FacebookURL < URL('http://www.facebook.com/__test_me__/foo/__test_again__')
|
67
|
+
allow_changed :subdomain
|
68
|
+
allow_params :foo, :bar
|
69
|
+
end
|
70
|
+
|
71
|
+
This will create a FacebookURL object where you can only change the subdomain, the params foo and bar, and the parts of the paths tagged
|
72
|
+
test_me and test_again. This object has the getters and setters: subdomain, foo, bar, test_me, and test_again. You can then call .to_s,
|
73
|
+
.get, .post, or .delete.
|
74
|
+
|
75
|
+
The object has an instance variable defined as @url which is a url object you can manipulate.
|
76
|
+
|
55
77
|
== TODO
|
78
|
+
* Fast Dup method
|
56
79
|
* More Documentation
|
57
80
|
* More specs
|
58
81
|
* Make faster
|
82
|
+
* JSON support
|
83
|
+
* More robust subdomain/path methods
|
59
84
|
|
60
85
|
== Note on Patches/Pull Requests
|
61
86
|
|
data/Rakefile
CHANGED
@@ -10,23 +10,17 @@ begin
|
|
10
10
|
gem.email = "me@tal.by"
|
11
11
|
gem.homepage = "http://github.com/talby/url"
|
12
12
|
gem.authors = ["Tal Atlas"]
|
13
|
-
gem.add_development_dependency "rspec", "
|
13
|
+
gem.add_development_dependency "rspec", "~> 2.0"
|
14
14
|
end
|
15
15
|
Jeweler::GemcutterTasks.new
|
16
16
|
rescue LoadError
|
17
17
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
18
|
end
|
19
19
|
|
20
|
-
require '
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
27
|
-
spec.libs << 'lib' << 'spec'
|
28
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
29
|
-
spec.rcov = true
|
20
|
+
require 'rspec/core/rake_task'
|
21
|
+
RSpec::Core::RakeTask.new do |t|
|
22
|
+
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
23
|
+
t.pattern = 'spec/**/*_spec.rb'
|
30
24
|
end
|
31
25
|
|
32
26
|
task :spec => :check_dependencies
|
data/VERSION.yml
CHANGED
data/lib/url/classer.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
module URL::Classer
|
2
|
+
VAR_MATCHER = /__([A-Za-z]?[A-Za-z_]*[A-Za-z])__/
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def allowed_params
|
6
|
+
@allowed_params ||= []
|
7
|
+
end
|
8
|
+
private
|
9
|
+
def allow_changed *args
|
10
|
+
args.flatten!
|
11
|
+
def_delegators :@url, :subdomains if args.include?(:subdomain)
|
12
|
+
def_delegators :@url, :subdomain if args.include?(:subdomains)
|
13
|
+
def_delegators :@url, :[], :[]= if args.include?(:params)
|
14
|
+
def_delegators :@url, *args
|
15
|
+
end
|
16
|
+
|
17
|
+
def allow_params *args
|
18
|
+
args.flatten.each do |arg|
|
19
|
+
arg = arg.to_sym
|
20
|
+
self.allowed_params << arg
|
21
|
+
define_method arg do
|
22
|
+
@url.params[arg]
|
23
|
+
end
|
24
|
+
|
25
|
+
define_method "#{arg}=" do |val|
|
26
|
+
@url.params[arg] = val
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def overrideable_path_val v
|
32
|
+
v = v.to_s.downcase
|
33
|
+
define_method v do
|
34
|
+
@var_map[v]
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method "#{v}=" do |val|
|
38
|
+
@var_map[v] = val
|
39
|
+
|
40
|
+
p = self.class.const_get(:URL).path.dup
|
41
|
+
|
42
|
+
@var_map.each do |key,value|
|
43
|
+
p.gsub!("__#{key}__", value.to_s)
|
44
|
+
end
|
45
|
+
|
46
|
+
@url.path = p
|
47
|
+
|
48
|
+
val
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
module InstanceMethods
|
55
|
+
def initialize(opts={})
|
56
|
+
@url = self.class.const_get(:URL).dup
|
57
|
+
|
58
|
+
@var_map = {}
|
59
|
+
|
60
|
+
opts.each do |op,v|
|
61
|
+
send("#{op}=",v)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
@url.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
def dup
|
70
|
+
n_url = @url.dup
|
71
|
+
n = super
|
72
|
+
n.instance_variable_set(:@url, n_url)
|
73
|
+
n
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.included(receiver)
|
79
|
+
receiver.extend Forwardable
|
80
|
+
receiver.send :def_delegators, :@url, :get, :post, :delete, :inspect
|
81
|
+
|
82
|
+
receiver.extend ClassMethods
|
83
|
+
receiver.send :include, InstanceMethods
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def URL url
|
88
|
+
if url.is_a?(URL)
|
89
|
+
url = url.dup
|
90
|
+
else
|
91
|
+
url = ::URL.new(url)
|
92
|
+
end
|
93
|
+
|
94
|
+
klass = Class.new do
|
95
|
+
include URL::Classer
|
96
|
+
|
97
|
+
vars = url.path.scan(URL::Classer::VAR_MATCHER).flatten
|
98
|
+
|
99
|
+
vars.each do |var|
|
100
|
+
overrideable_path_val(var)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
klass.const_set(:URL, url.freeze)
|
105
|
+
|
106
|
+
klass
|
107
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class URL
|
2
|
+
# Net::HTTP Handler
|
3
|
+
class NetHandler < RequestHandler
|
4
|
+
def get(args={})
|
5
|
+
http = http_obj
|
6
|
+
request = Net::HTTP::Get.new(make_path + url.params.to_s)
|
7
|
+
t = Time.now
|
8
|
+
resp = http.request(request)
|
9
|
+
make_str(resp,Time.now-t)
|
10
|
+
end
|
11
|
+
|
12
|
+
def post(args={})
|
13
|
+
http = http_obj
|
14
|
+
request = Net::HTTP::Post.new(make_path)
|
15
|
+
request.set_form_data(url.params)
|
16
|
+
t = Time.now
|
17
|
+
resp = http.request(request)
|
18
|
+
make_str(resp,Time.now-t)
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete(args={})
|
22
|
+
http = http_obj
|
23
|
+
request = Net::HTTP::Delete.new(make_path + url.params.to_s)
|
24
|
+
t = Time.now
|
25
|
+
resp = http.request(request)
|
26
|
+
make_str(resp,Time.now-t)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def make_path
|
32
|
+
url.path
|
33
|
+
end
|
34
|
+
|
35
|
+
def make_str(resp,time)
|
36
|
+
hsh = {
|
37
|
+
:code => resp.code.to_i,
|
38
|
+
:time => time,
|
39
|
+
:body => resp.body,
|
40
|
+
:response => resp,
|
41
|
+
:url => url.to_s
|
42
|
+
}
|
43
|
+
|
44
|
+
Response.new(hsh)
|
45
|
+
end
|
46
|
+
|
47
|
+
def http_obj
|
48
|
+
uri = url.to_uri
|
49
|
+
http = Net::HTTP.new(uri.host,uri.port)
|
50
|
+
|
51
|
+
if url.scheme == 'https'
|
52
|
+
http.use_ssl = true
|
53
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
54
|
+
end
|
55
|
+
|
56
|
+
http
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class URL
|
2
|
+
# Typhoeus handler
|
3
|
+
class TyHandler < RequestHandler
|
4
|
+
|
5
|
+
def get(args={})
|
6
|
+
resp = Typhoeus::Request.get(url.to_s)
|
7
|
+
|
8
|
+
make_str(resp)
|
9
|
+
end
|
10
|
+
|
11
|
+
def post(args={})
|
12
|
+
resp = Typhoeus::Request.post(url.to_s(:params => false), :params => url.params)
|
13
|
+
|
14
|
+
make_str(resp)
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete(args={})
|
18
|
+
resp = Typhoeus::Request.delete(url.to_s)
|
19
|
+
make_str(resp)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def make_str(resp)
|
25
|
+
hsh = {
|
26
|
+
:code => resp.code,
|
27
|
+
:time => resp.time,
|
28
|
+
:body => resp.body,
|
29
|
+
:response => resp,
|
30
|
+
:url => url.to_s
|
31
|
+
}
|
32
|
+
|
33
|
+
Response.new(hsh)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/url/handlers.rb
CHANGED
@@ -13,100 +13,37 @@ class URL
|
|
13
13
|
# :url => url.to_s
|
14
14
|
# }
|
15
15
|
# Response.new(hsh)
|
16
|
-
class
|
16
|
+
class RequestHandler
|
17
17
|
attr_reader :url
|
18
18
|
def initialize(url)
|
19
19
|
@url = url
|
20
20
|
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# Typhoeus handler
|
24
|
-
class TyHandler < Handler
|
25
21
|
|
26
22
|
def get(args={})
|
27
|
-
|
28
|
-
|
29
|
-
make_str(resp)
|
23
|
+
raise Exception, "You need to implement #{self.class}#get"
|
30
24
|
end
|
31
25
|
|
32
26
|
def post(args={})
|
33
|
-
|
34
|
-
|
35
|
-
make_str(resp)
|
27
|
+
raise Exception, "You need to implement #{self.class}#post"
|
36
28
|
end
|
37
29
|
|
38
|
-
def delete
|
39
|
-
|
40
|
-
make_str(resp)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def make_str(resp)
|
46
|
-
hsh = {
|
47
|
-
:code => resp.code,
|
48
|
-
:time => resp.time,
|
49
|
-
:body => resp.body,
|
50
|
-
:response => resp,
|
51
|
-
:url => url.to_s
|
52
|
-
}
|
53
|
-
|
54
|
-
Response.new(hsh)
|
30
|
+
def delete(args={})
|
31
|
+
raise Exception, "You need to implement #{self.class}#delete"
|
55
32
|
end
|
56
|
-
|
57
33
|
end
|
58
34
|
|
59
|
-
|
60
|
-
|
61
|
-
def
|
62
|
-
|
63
|
-
request = Net::HTTP::Get.new(url.path + url.params.to_s)
|
64
|
-
t = Time.now
|
65
|
-
resp = http.request(request)
|
66
|
-
make_str(resp,Time.now-t)
|
35
|
+
class JSONHandler
|
36
|
+
attr_reader :str
|
37
|
+
def initialize(str)
|
38
|
+
@str = str
|
67
39
|
end
|
68
40
|
|
69
|
-
def
|
70
|
-
|
71
|
-
request = Net::HTTP::Post.new(url.path)
|
72
|
-
request.set_form_data(url.params)
|
73
|
-
t = Time.now
|
74
|
-
resp = http.request(request)
|
75
|
-
make_str(resp,Time.now-t)
|
41
|
+
def parse
|
42
|
+
raise Exception, "You need to implement #{self.class}#parse"
|
76
43
|
end
|
77
44
|
|
78
|
-
def delete(args={})
|
79
|
-
http = http_obj
|
80
|
-
request = Net::HTTP::Delete.new(url.path + url.params.to_s)
|
81
|
-
t = Time.now
|
82
|
-
resp = http.request(request)
|
83
|
-
make_str(resp,Time.now-t)
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def make_str(resp,time)
|
89
|
-
hsh = {
|
90
|
-
:code => resp.code.to_i,
|
91
|
-
:time => time,
|
92
|
-
:body => resp.body,
|
93
|
-
:response => resp,
|
94
|
-
:url => url.to_s
|
95
|
-
}
|
96
|
-
|
97
|
-
Response.new(hsh)
|
98
|
-
end
|
99
|
-
|
100
|
-
def http_obj
|
101
|
-
uri = url.to_uri
|
102
|
-
http = Net::HTTP.new(uri.host,uri.port)
|
103
|
-
|
104
|
-
if url.scheme == 'https'
|
105
|
-
http.use_ssl = true
|
106
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
107
|
-
end
|
108
|
-
|
109
|
-
http
|
110
|
-
end
|
111
45
|
end
|
112
|
-
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
Dir[File.join(File.dirname(__FILE__),'handlers','*.rb')].each {|f| require f}
|
data/lib/url/helper_classes.rb
CHANGED
@@ -1,68 +1,50 @@
|
|
1
|
-
require "delegate"
|
2
|
-
|
3
1
|
class URL
|
4
2
|
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# * success? - whether the http code is 200
|
12
|
-
# * url - the URL the object was gotten from
|
13
|
-
class Response < DelegateClass(String)
|
14
|
-
# The time taken for the request
|
15
|
-
# @returns [Integer]
|
16
|
-
attr_reader :time
|
17
|
-
|
18
|
-
# The http code return
|
19
|
-
# @returns [Integer] eg. 200, 404, 500, 503
|
20
|
-
attr_reader :code
|
21
|
-
|
22
|
-
# The response object generated by the handler
|
23
|
-
# @returns [Net::HTTPResponse,Typhoeus::Response]
|
24
|
-
attr_reader :response
|
25
|
-
|
26
|
-
# The url which generated this response
|
27
|
-
# @returns [String]
|
28
|
-
attr_reader :url
|
29
|
-
|
30
|
-
# @param [String] body The body of the response object, main string
|
31
|
-
# @param [Hash] args Additional arguments: :time,:code,:response,:url
|
32
|
-
def initialize(str,args={})
|
33
|
-
if str.is_a?(Hash)
|
34
|
-
args = str
|
35
|
-
str = args[:body]
|
36
|
-
end
|
37
|
-
|
38
|
-
raise unless str
|
39
|
-
super(str)
|
40
|
-
args.each do |key, value|
|
41
|
-
instance_variable_set "@#{key}", value
|
42
|
-
end
|
3
|
+
# A hash where all keys are symbols
|
4
|
+
class Mash < Hash
|
5
|
+
# Set the value of a param
|
6
|
+
def []=(k,v)
|
7
|
+
k = k.to_s.to_sym unless k.is_a?(Symbol)
|
8
|
+
super(k,v)
|
43
9
|
end
|
44
10
|
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
code == 200
|
11
|
+
# Read the value of a param
|
12
|
+
def [](k)
|
13
|
+
k = k.to_s.to_sym unless k.is_a?(Symbol)
|
14
|
+
super(k)
|
51
15
|
end
|
52
16
|
end
|
53
17
|
|
54
|
-
class ParamsHash <
|
18
|
+
class ParamsHash < Mash
|
55
19
|
|
56
20
|
# Merges the array into a parameter string of the form <tt>?key=value&foo=bar</tt>
|
57
21
|
def to_s
|
58
22
|
return '' if empty?
|
59
23
|
'?' + to_a.inject(Array.new) do |ret,param|
|
60
|
-
|
61
|
-
|
62
|
-
val = CGI.escape(val)# if val =~ /(\/|\?|\s)/
|
24
|
+
key = param[0].to_s
|
25
|
+
val = param[1]
|
63
26
|
|
64
27
|
if param && val
|
65
|
-
|
28
|
+
if val.is_a?(Hash)
|
29
|
+
# TODO: Make this recusrive
|
30
|
+
val.each do |param_key,param_val|
|
31
|
+
param_key = CGI.escape("#{key}[#{param_key}]")
|
32
|
+
param_val = CGI.escape(param_val.to_s)
|
33
|
+
ret << %Q{#{param_key}=#{param_val}}
|
34
|
+
end
|
35
|
+
elsif val.is_a?(Array)
|
36
|
+
# TODO: Make this recusrive
|
37
|
+
val.each_with_index do |param_val,i|
|
38
|
+
param_key = CGI.escape("#{key}[]")
|
39
|
+
param_val = CGI.escape(param_val.to_s)
|
40
|
+
ret << %Q{#{param_key}=#{param_val}}
|
41
|
+
end
|
42
|
+
else
|
43
|
+
val = val.to_s
|
44
|
+
|
45
|
+
val = CGI.escape(val)# if val =~ /(\/|\?|\s)/
|
46
|
+
ret << %{#{param[0].to_s}=#{val}}
|
47
|
+
end
|
66
48
|
elsif param
|
67
49
|
ret << param[0].to_s
|
68
50
|
end
|
data/lib/url/response.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
class URL
|
4
|
+
|
5
|
+
# The Response class is a deleegate to string which also contains metadata about the request.
|
6
|
+
# These methods are also available
|
7
|
+
# * body
|
8
|
+
# * code - http code
|
9
|
+
# * response - the original response object from whatever handler you chose
|
10
|
+
# * time - time taken to make call
|
11
|
+
# * success? - whether the http code is 2xx
|
12
|
+
# * url - the URL the object was gotten from
|
13
|
+
class Response < DelegateClass(String)
|
14
|
+
# The time taken for the request
|
15
|
+
# @returns [Integer]
|
16
|
+
attr_reader :time
|
17
|
+
|
18
|
+
# The http code return
|
19
|
+
# @returns [Integer] eg. 200, 404, 500, 503
|
20
|
+
attr_reader :code
|
21
|
+
|
22
|
+
# The response object generated by the handler
|
23
|
+
# @returns [Net::HTTPResponse,Typhoeus::Response]
|
24
|
+
attr_reader :response
|
25
|
+
|
26
|
+
# The url which generated this response
|
27
|
+
# @returns [String]
|
28
|
+
attr_reader :url
|
29
|
+
|
30
|
+
# @param [String] body The body of the response object, main string
|
31
|
+
# @param [Hash] args Additional arguments: :time,:code,:response,:url
|
32
|
+
def initialize(str,args={})
|
33
|
+
if str.is_a?(Hash)
|
34
|
+
args = str
|
35
|
+
str = args[:body]
|
36
|
+
end
|
37
|
+
|
38
|
+
raise unless str
|
39
|
+
super(str)
|
40
|
+
args.each do |key, value|
|
41
|
+
instance_variable_set "@#{key}", value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Compares {Response#code} to 2xx
|
46
|
+
# @returns [true,false]
|
47
|
+
def success?
|
48
|
+
return @successful if @successful
|
49
|
+
|
50
|
+
(200..299).include?(code)
|
51
|
+
end
|
52
|
+
|
53
|
+
def json
|
54
|
+
raise StandardError, 'No JSON library initialized' unless URL.json_handler
|
55
|
+
URL.json_handler.new(self).parse
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/url.rb
CHANGED
@@ -2,8 +2,11 @@ require "net/http"
|
|
2
2
|
require "net/https"
|
3
3
|
require 'uri'
|
4
4
|
require 'cgi'
|
5
|
+
require 'forwardable'
|
5
6
|
|
6
|
-
|
7
|
+
|
8
|
+
files = Dir.glob(File.join(File.dirname(__FILE__),'url','*.rb'))
|
9
|
+
files.delete_if {|f| f =~ /url\/(classer)\.rb/}
|
7
10
|
files.each { |f| require f }
|
8
11
|
|
9
12
|
# Main class for managing urls
|
@@ -20,12 +23,45 @@ files.each { |f| require f }
|
|
20
23
|
# url.params[:foo] = 'bar'
|
21
24
|
# url.to_s # => 'https://my.mail.google.com/mail/?foo=bar&shva=1#mbox'
|
22
25
|
class URL
|
23
|
-
|
26
|
+
extend Forwardable
|
27
|
+
attr_reader :string
|
28
|
+
|
29
|
+
# The params for the request
|
30
|
+
# @returns [URL::ParamsHash]
|
31
|
+
attr_reader :params
|
24
32
|
|
25
33
|
# Attributes of the URL which are editable
|
26
|
-
|
34
|
+
# @returns [String]
|
35
|
+
attr_accessor :domain, :scheme, :format, :port, :hash
|
36
|
+
|
37
|
+
# The path for the request
|
38
|
+
# @returns [URL::Path]
|
39
|
+
attr_reader :path
|
40
|
+
|
41
|
+
# Set the path for the request
|
42
|
+
def path=str
|
43
|
+
if str.nil? || str.empty?
|
44
|
+
str = '/'
|
45
|
+
end
|
46
|
+
|
47
|
+
@path = str
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns array of subdomains
|
51
|
+
# @returns [URL::Subdomain]
|
52
|
+
attr_reader :subdomain
|
27
53
|
alias_method :subdomains, :subdomain
|
28
54
|
|
55
|
+
# @param [Array,String] subdomain An array or string for subdomain
|
56
|
+
def subdomain=(s)
|
57
|
+
if s.is_a?(String)
|
58
|
+
s = s.split('.')
|
59
|
+
end
|
60
|
+
|
61
|
+
@subdomain = s
|
62
|
+
end
|
63
|
+
alias_method :subdomains=, :subdomain=
|
64
|
+
|
29
65
|
# Creates a new URL object
|
30
66
|
# @param [String] URL the starting url to work with
|
31
67
|
def initialize str
|
@@ -33,7 +69,7 @@ class URL
|
|
33
69
|
sp = URI.split(@string)
|
34
70
|
@scheme = sp[0]
|
35
71
|
@port = sp[3]
|
36
|
-
|
72
|
+
self.path = sp[5]
|
37
73
|
@format = @path.gsub(/(.+\.)/,'')
|
38
74
|
@hash = sp[8]
|
39
75
|
|
@@ -54,18 +90,20 @@ class URL
|
|
54
90
|
@domain = nil
|
55
91
|
@subdomain = nil
|
56
92
|
end
|
93
|
+
|
94
|
+
@params = ParamsHash.new
|
57
95
|
if sp[7]
|
58
|
-
|
59
|
-
key,value =
|
96
|
+
sp[7].gsub('?','').split('&').each do |myp|
|
97
|
+
key,value = myp.split('=')
|
60
98
|
value = CGI.unescape(value) if value
|
61
|
-
|
62
|
-
result
|
99
|
+
@params[key.to_sym] = value if key
|
63
100
|
end
|
64
|
-
else
|
65
|
-
@params = ParamsHash.new
|
66
101
|
end
|
67
102
|
end
|
68
103
|
|
104
|
+
def_delegators :@params, :[], :[]=
|
105
|
+
|
106
|
+
# The full hostname (not including port) for the URL
|
69
107
|
def host
|
70
108
|
[@subdomain,@domain].flatten.compact.join('.')
|
71
109
|
end
|
@@ -97,8 +135,41 @@ class URL
|
|
97
135
|
|
98
136
|
class << self
|
99
137
|
# Define the request handler to use. If Typhoeus is setup it will use {TyHandler} otherwise will default back to Net::HTTP with {NetHandler}
|
100
|
-
# @return [
|
101
|
-
|
138
|
+
# @return [RequstHandler]
|
139
|
+
def req_handler
|
140
|
+
return @req_handler if @req_handler
|
141
|
+
|
142
|
+
if defined?(Typhoeus)
|
143
|
+
URL.req_handler = URL::TyHandler
|
144
|
+
else
|
145
|
+
URL.req_handler = URL::NetHandler
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Define the request handler to use. If Typhoeus is setup it will use {TyHandler} otherwise will default back to Net::HTTP with {NetHandler}
|
150
|
+
# @param [RequstHandler]
|
151
|
+
# @return [RequstHandler]
|
152
|
+
def req_handler=r
|
153
|
+
raise ArgumentError, 'Must be a subclass of URL::RequestHandler' unless r.nil? || r < RequestHandler
|
154
|
+
@req_handler = r
|
155
|
+
end
|
156
|
+
|
157
|
+
def json_handler
|
158
|
+
return @json_handler if @json_handler
|
159
|
+
|
160
|
+
if defined?(Yajl)
|
161
|
+
URL.json_handler = URL::YajlHandler
|
162
|
+
elsif defined?(JSON)
|
163
|
+
URL.json_handler = URL::BaseJSONHandler
|
164
|
+
elsif defined?(ActiveSupport::JSON)
|
165
|
+
URL.json_handler = URL::ASJSONHandler
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def json_handler=r
|
170
|
+
raise ArgumentError, 'Must be a subclass of URL::JSONHandler' unless r.nil? || r < JSONHandler
|
171
|
+
@json_handler = r
|
172
|
+
end
|
102
173
|
end
|
103
174
|
|
104
175
|
# Performs a get request for the current URL
|
@@ -120,18 +191,30 @@ class URL
|
|
120
191
|
end
|
121
192
|
|
122
193
|
def inspect
|
123
|
-
"
|
194
|
+
"#<#{self.class} #{to_s}>"
|
124
195
|
end
|
125
196
|
|
126
|
-
|
127
|
-
URL.
|
128
|
-
else
|
129
|
-
URL.req_handler = NetHandler
|
197
|
+
def dup
|
198
|
+
URL.new(to_s)
|
130
199
|
end
|
131
200
|
|
132
|
-
|
201
|
+
# The request handler for this
|
202
|
+
# @return [Handler]
|
133
203
|
def req_handler
|
134
|
-
self.class.req_handler.new(self)
|
204
|
+
(@req_handler||self.class.req_handler).new(self)
|
205
|
+
end
|
206
|
+
|
207
|
+
def =~ reg
|
208
|
+
to_s =~ reg
|
135
209
|
end
|
210
|
+
|
211
|
+
# Sets the handler to use for this request
|
212
|
+
# @param [RequstHandler]
|
213
|
+
# @return [RequstHandler]
|
214
|
+
def req_handler=r
|
215
|
+
raise ArgumentError, 'Must be a subclass of URL::Handler' unless r < RequestHandler
|
216
|
+
@req_handler = r
|
217
|
+
end
|
218
|
+
|
136
219
|
end
|
137
220
|
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'url/classer'
|
3
|
+
|
4
|
+
describe "URL()" do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
class FacebookURL < URL('http://www.facebook.com/__test_me__/foo/__test_again__')
|
8
|
+
allow_changed :subdomain
|
9
|
+
allow_params :foo, :bar
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should create a class" do
|
14
|
+
url = 'http://www.facebook.com'
|
15
|
+
lambda {class FacebookURL2 < URL(url); end}.should_not raise_error
|
16
|
+
|
17
|
+
FacebookURL2.new.to_s.should == 'http://www.facebook.com/'
|
18
|
+
FacebookURL2.new.should be_a(URL::Classer)
|
19
|
+
end
|
20
|
+
|
21
|
+
context '.new' do
|
22
|
+
subject {FacebookURL.new}
|
23
|
+
|
24
|
+
it "shoud work like whatever" do
|
25
|
+
subject.subdomain << 'us'
|
26
|
+
subject.to_s.should =~ /^http:\/\/www\.us\.facebook.com/
|
27
|
+
|
28
|
+
u = FacebookURL.new
|
29
|
+
u.to_s.should =~ /^http:\/\/www\.facebook.com/
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should dup" do
|
33
|
+
subject.subdomain << 'us'
|
34
|
+
d = subject.dup
|
35
|
+
d.subdomain.should == ['www','us']
|
36
|
+
|
37
|
+
d.subdomain << '1'
|
38
|
+
d.subdomain.should == ['www','us','1']
|
39
|
+
subject.subdomain.should == ['www','us']
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should allow params" do
|
43
|
+
subject.foo = 1
|
44
|
+
subject.foo.should == 1
|
45
|
+
subject.to_s.should =~ /foo=1/
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should set vars" do
|
49
|
+
subject.test_me = 'foobar'
|
50
|
+
subject.test_me.should == 'foobar'
|
51
|
+
|
52
|
+
subject.test_again = 'abc'
|
53
|
+
subject.to_s.should == "http://www.facebook.com/foobar/foo/abc"
|
54
|
+
|
55
|
+
subject.test_again = 'aaa'
|
56
|
+
subject.to_s.should == "http://www.facebook.com/foobar/foo/aaa"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should set vars in create" do
|
60
|
+
u = FacebookURL.new(:test_me => 'foobar', :test_again => 'abc')
|
61
|
+
|
62
|
+
u.to_s.should == "http://www.facebook.com/foobar/foo/abc"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
shared_examples_for "all requests" do
|
4
|
+
it "should work" do
|
5
|
+
@resp.should be_success
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should have a response of the correct class" do
|
9
|
+
@resp.response.should be_a(@resp_class)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "shoudl have all attribures" do
|
13
|
+
@resp.time.should be_a(Float)
|
14
|
+
@resp.code.should be_a(Integer)
|
15
|
+
@resp.url.should be_a(String)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples_for "all builds" do
|
20
|
+
|
21
|
+
|
22
|
+
before do
|
23
|
+
@url = URL.new('http://www.omgpop.com')
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#get" do
|
27
|
+
before do
|
28
|
+
@resp = @url.get
|
29
|
+
end
|
30
|
+
it_should_behave_like "all requests"
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#post" do
|
34
|
+
before do
|
35
|
+
@resp = @url.post
|
36
|
+
end
|
37
|
+
it_should_behave_like "all requests"
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#delete" do
|
41
|
+
before do
|
42
|
+
@resp = @url.delete
|
43
|
+
end
|
44
|
+
it_should_behave_like "all requests"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "Typhoeus", URL do
|
49
|
+
before(:all) do
|
50
|
+
require 'typhoeus'
|
51
|
+
URL.req_handler = URL::TyHandler
|
52
|
+
@resp_class = Typhoeus::Response
|
53
|
+
end
|
54
|
+
|
55
|
+
it_should_behave_like "all builds"
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
describe "Net::HTTP", URL do
|
61
|
+
before(:all) do
|
62
|
+
require 'net/http'
|
63
|
+
URL.req_handler = URL::NetHandler
|
64
|
+
@resp_class = Net::HTTPResponse
|
65
|
+
end
|
66
|
+
|
67
|
+
it_should_behave_like "all builds"
|
68
|
+
|
69
|
+
end
|
data/spec/json_spec.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'json'
|
2
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
3
|
+
|
4
|
+
describe URL do
|
5
|
+
context "A json response" do
|
6
|
+
subject { URL.new('https://graph.facebook.com/37901410') }
|
7
|
+
|
8
|
+
it "should parse json" do
|
9
|
+
subject.get.json.should be_a(Hash)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/url_spec.rb
CHANGED
@@ -28,7 +28,11 @@ describe URL do
|
|
28
28
|
|
29
29
|
describe '#to_s' do
|
30
30
|
it "should roundtrip" do
|
31
|
-
@url.to_s
|
31
|
+
s = @url.to_s
|
32
|
+
s.should include 'https://mail.google.com:8080/foo/bar/baz?'
|
33
|
+
s.should include 'q=one'
|
34
|
+
s.should include 'foo=bar'
|
35
|
+
s.should include '&'
|
32
36
|
end
|
33
37
|
|
34
38
|
it "should change and add params" do
|
@@ -46,72 +50,80 @@ describe URL do
|
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
49
|
-
end
|
50
|
-
|
51
|
-
shared_examples_for "all requests" do
|
52
|
-
it "should work" do
|
53
|
-
@resp.should be_success
|
54
|
-
end
|
55
|
-
|
56
|
-
it "should have a response of the correct class" do
|
57
|
-
@resp.response.should be_a(@resp_class)
|
58
|
-
end
|
59
53
|
|
60
|
-
it "
|
61
|
-
@
|
62
|
-
@
|
63
|
-
@
|
54
|
+
it "should should modify params easily" do
|
55
|
+
@url[:foo] = '123'
|
56
|
+
@url['foo'].should == '123'
|
57
|
+
@url.to_s.should include('foo=123')
|
58
|
+
|
59
|
+
@url['foo'] = '345'
|
60
|
+
@url[:foo].should == '345'
|
61
|
+
@url.to_s.should include('foo=345')
|
64
62
|
end
|
65
|
-
end
|
66
|
-
|
67
|
-
shared_examples_for "all builds" do
|
68
63
|
|
69
|
-
|
70
|
-
|
71
|
-
@url =
|
64
|
+
it "should should accept arbitrary req handlers" do
|
65
|
+
class TestReq < URL::TyHandler; end
|
66
|
+
@url.req_handler = TestReq
|
67
|
+
@url.req_handler.should be_a(URL::RequestHandler)
|
68
|
+
@url.req_handler.should be_instance_of(TestReq)
|
72
69
|
end
|
73
70
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
71
|
+
it "should should make sure there's always a path" do
|
72
|
+
@url.path = nil
|
73
|
+
@url.path.should == '/'
|
74
|
+
|
75
|
+
@url.path = ''
|
76
|
+
@url.path.should == '/'
|
79
77
|
end
|
80
78
|
|
81
|
-
|
82
|
-
|
83
|
-
@resp = @url.post
|
84
|
-
end
|
85
|
-
it_should_behave_like "all requests"
|
79
|
+
it "should match =~" do
|
80
|
+
(@url =~ /mail\.google\.com/).should == (@url.to_s =~ /mail\.google\.com/)
|
86
81
|
end
|
87
82
|
|
88
|
-
describe "#delete" do
|
89
|
-
before do
|
90
|
-
@resp = @url.delete
|
91
|
-
end
|
92
|
-
it_should_behave_like "all requests"
|
93
|
-
end
|
94
83
|
end
|
95
84
|
|
96
|
-
describe
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
85
|
+
describe URL::ParamsHash, '#to_s' do
|
86
|
+
it "should make a param string" do
|
87
|
+
hsh = URL::ParamsHash.new
|
88
|
+
|
89
|
+
hsh[:foo] = 'bar'
|
90
|
+
hsh[1] = 2
|
91
|
+
|
92
|
+
str = hsh.to_s
|
93
|
+
|
94
|
+
# str.should match(/^?/)
|
95
|
+
str.should include('foo=bar')
|
96
|
+
str.should include('1=2')
|
101
97
|
end
|
102
98
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
99
|
+
it "should make a param string with an array or hash" do
|
100
|
+
hsh = URL::ParamsHash.new
|
101
|
+
|
102
|
+
hsh[:test] = [*1..3]
|
103
|
+
hsh[:hash] = {:one => 1, :two => 2}
|
104
|
+
|
105
|
+
str = hsh.to_s
|
106
|
+
|
107
|
+
str.should include CGI.escape('hash[one]')+'=1'
|
108
|
+
str.should include CGI.escape('hash[two]')+'=2'
|
109
|
+
str.should include CGI.escape('test[]')+'=1'
|
110
|
+
str.should include CGI.escape('test[]')+'=2'
|
111
|
+
str.should include CGI.escape('test[]')+'=3'
|
112
|
+
end
|
116
113
|
|
114
|
+
it "should recursively make objects" do
|
115
|
+
pending('implementation')
|
116
|
+
hsh = URL::ParamsHash.new
|
117
|
+
|
118
|
+
hsh[:test] = [{:foo => 'bar', :bar => 'baz'},{:foo => 'baz'}]
|
119
|
+
hsh[:hash] = {:one => {'o' => 1}, :two => 2}
|
120
|
+
|
121
|
+
str = hsh.to_s
|
122
|
+
|
123
|
+
str.should include CGI.escape('test[][foo]')+'=bar'
|
124
|
+
str.should include CGI.escape('test[][bar]')+'=baz'
|
125
|
+
str.should include CGI.escape('test[][foo]')+'=baz'
|
126
|
+
str.should include CGI.escape('hash[one][o]')+'=1'
|
127
|
+
str.should include CGI.escape('hash[two]')+'=2'
|
128
|
+
end
|
117
129
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: url
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 1
|
9
8
|
- 2
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tal Atlas
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-31 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -24,14 +24,13 @@ dependencies:
|
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 3
|
30
30
|
segments:
|
31
|
-
- 1
|
32
31
|
- 2
|
33
|
-
-
|
34
|
-
version:
|
32
|
+
- 0
|
33
|
+
version: "2.0"
|
35
34
|
type: :development
|
36
35
|
version_requirements: *id001
|
37
36
|
description: A simple url object to allow for OO based manipulation and usage of a url
|
@@ -45,14 +44,23 @@ extra_rdoc_files:
|
|
45
44
|
- README.rdoc
|
46
45
|
files:
|
47
46
|
- .document
|
48
|
-
- .gitignore
|
49
47
|
- LICENSE
|
50
48
|
- README.rdoc
|
51
49
|
- Rakefile
|
52
50
|
- VERSION.yml
|
53
51
|
- lib/url.rb
|
52
|
+
- lib/url/classer.rb
|
54
53
|
- lib/url/handlers.rb
|
54
|
+
- lib/url/handlers/as_json_handler.rb
|
55
|
+
- lib/url/handlers/base_json_handler.rb
|
56
|
+
- lib/url/handlers/net_handler.rb
|
57
|
+
- lib/url/handlers/ty_handler.rb
|
58
|
+
- lib/url/handlers/yajl_handler.rb
|
55
59
|
- lib/url/helper_classes.rb
|
60
|
+
- lib/url/response.rb
|
61
|
+
- spec/classer_spec.rb
|
62
|
+
- spec/handler_spec.rb
|
63
|
+
- spec/json_spec.rb
|
56
64
|
- spec/spec.opts
|
57
65
|
- spec/spec_helper.rb
|
58
66
|
- spec/url_spec.rb
|
@@ -61,8 +69,8 @@ homepage: http://github.com/talby/url
|
|
61
69
|
licenses: []
|
62
70
|
|
63
71
|
post_install_message:
|
64
|
-
rdoc_options:
|
65
|
-
|
72
|
+
rdoc_options: []
|
73
|
+
|
66
74
|
require_paths:
|
67
75
|
- lib
|
68
76
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -91,5 +99,8 @@ signing_key:
|
|
91
99
|
specification_version: 3
|
92
100
|
summary: A URL object
|
93
101
|
test_files:
|
102
|
+
- spec/classer_spec.rb
|
103
|
+
- spec/handler_spec.rb
|
104
|
+
- spec/json_spec.rb
|
94
105
|
- spec/spec_helper.rb
|
95
106
|
- spec/url_spec.rb
|
data/.gitignore
DELETED