url 0.1.2 → 0.2.0
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.
- 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