relax 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +63 -0
- data/Gemfile +3 -0
- data/LICENSE +3 -1
- data/README.md +80 -0
- data/Rakefile +2 -52
- data/examples/delicious.rb +61 -0
- data/examples/flickr.rb +66 -0
- data/examples/vimeo.rb +65 -0
- data/lib/relax.rb +6 -16
- data/lib/relax/client.rb +18 -0
- data/lib/relax/config.rb +12 -0
- data/lib/relax/delegator.rb +25 -0
- data/lib/relax/resource.rb +32 -0
- data/lib/relax/version.rb +3 -0
- data/relax.gemspec +17 -0
- data/spec/relax/client_spec.rb +39 -0
- data/spec/relax/config_spec.rb +17 -0
- data/spec/relax/delegator_spec.rb +32 -0
- data/spec/relax/resource_spec.rb +60 -0
- data/spec/spec_helper.rb +9 -13
- metadata +75 -96
- data/README.rdoc +0 -194
- data/VERSION.yml +0 -4
- data/lib/relax/action.rb +0 -49
- data/lib/relax/context.rb +0 -46
- data/lib/relax/contextable.rb +0 -15
- data/lib/relax/endpoint.rb +0 -21
- data/lib/relax/instance.rb +0 -25
- data/lib/relax/parameter.rb +0 -23
- data/lib/relax/performer.rb +0 -58
- data/lib/relax/service.rb +0 -45
- data/spec/relax/context_spec.rb +0 -18
- data/spec/relax/endpoint_spec.rb +0 -99
- data/spec/relax/integration_spec.rb +0 -63
- data/spec/relax/service_spec.rb +0 -32
- data/spec/services/blank_values_service.rb +0 -19
- data/spec/services/custom_parser_service.rb +0 -24
- data/spec/services/flickr.rb +0 -78
- data/spec/services/parameter_alias_service.rb +0 -17
data/README.rdoc
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
= Relax
|
2
|
-
|
3
|
-
Relax is a library that provides a foundation for writing REST consumer APIs,
|
4
|
-
including the logic to handle the HTTP requests, build URLs with query
|
5
|
-
parameters, and parse responses.
|
6
|
-
|
7
|
-
|
8
|
-
== Tutorial
|
9
|
-
|
10
|
-
This short tutorial will walk you through the basic steps of creating a simple Flickr API consumer that supports a single call to search for photos by tags.
|
11
|
-
|
12
|
-
|
13
|
-
=== First Things First
|
14
|
-
|
15
|
-
The first step we'll take is to load the Relax gem.
|
16
|
-
|
17
|
-
require 'rubygems'
|
18
|
-
|
19
|
-
gem 'relax', '~> 0.1.0'
|
20
|
-
require 'relax'
|
21
|
-
|
22
|
-
Then we'll define our service class.
|
23
|
-
|
24
|
-
class Flickr < Relax::Service
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
=== Adding an Endpoint
|
29
|
-
|
30
|
-
An endpoint is the base URL for the service we'll be consuming. All of our
|
31
|
-
actions will be nested inside of an endpoint and build on top of it to
|
32
|
-
forumlate the final request URL.
|
33
|
-
|
34
|
-
class Flickr < Relax::Service
|
35
|
-
endpoint 'http://api.flickr.com/services/rest/' do
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
=== Adding Default Parameters
|
41
|
-
|
42
|
-
Since Flickr requires us to always pass an API key parameter let's make that a
|
43
|
-
required default parameter in our service class.
|
44
|
-
|
45
|
-
class Flickr < Relax::Service
|
46
|
-
defaults do
|
47
|
-
parameter :api_key, :required => true
|
48
|
-
end
|
49
|
-
|
50
|
-
endpoint 'http://api.flickr.com/services/rest/' do
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
=== Adding an Action
|
56
|
-
|
57
|
-
So we have our service now, but we need to define an action before we can
|
58
|
-
actually fetch any data from it.
|
59
|
-
|
60
|
-
class Flickr < Relax::Service
|
61
|
-
defaults do
|
62
|
-
parameter :api_key, :required => true
|
63
|
-
end
|
64
|
-
|
65
|
-
endpoint 'http://api.flickr.com/services/rest/' do
|
66
|
-
action :search do
|
67
|
-
parameter :method, :default => 'flickr.photos.search'
|
68
|
-
parameter :per_page, :default => 5
|
69
|
-
parameter :tags
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
We're defining an action here called <tt>:search</tt> that will create an
|
75
|
-
instance method on our service with the same name. There are three parameters,
|
76
|
-
<tt>:method</tt>, <tt>:per_page</tt>, and <tt>:tags</tt>, with <tt>:method</tt>
|
77
|
-
and <tt>:per_page</tt> receiving default values.
|
78
|
-
|
79
|
-
There are lots of other parameters for the <tt>flickr.photos.search</tt> method
|
80
|
-
that we could define, but we'll just stick with these for simplicities sake.
|
81
|
-
|
82
|
-
|
83
|
-
=== A Small Refactoring
|
84
|
-
|
85
|
-
When adding more methods it will quickly become obvious that we have another
|
86
|
-
common parameter in <tt>:method</tt>. We can refactor our service class
|
87
|
-
slightly to make this a default parameter for all of the actions on our
|
88
|
-
endpoint.
|
89
|
-
|
90
|
-
class Flickr < Relax::Service
|
91
|
-
defaults do
|
92
|
-
parameter :api_key, :required => true
|
93
|
-
end
|
94
|
-
|
95
|
-
endpoint 'http://api.flickr.com/services/rest/' do
|
96
|
-
defaults do
|
97
|
-
parameter :method, :required => true
|
98
|
-
end
|
99
|
-
|
100
|
-
action :search do
|
101
|
-
set :method, 'flickr.photos.search'
|
102
|
-
parameter :per_page, :default => 5
|
103
|
-
parameter :tags
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
The <tt>set</tt> method allows us to define a default value for a default
|
109
|
-
parameter so we don't have to redefine common parameters inside every action.
|
110
|
-
|
111
|
-
|
112
|
-
=== Defining a Parser
|
113
|
-
|
114
|
-
Before we can actually perform a request we need to let the service know how to
|
115
|
-
handle the response. This is done by defining a parser.
|
116
|
-
|
117
|
-
class Flickr < Relax::Service
|
118
|
-
defaults do
|
119
|
-
parameter :api_key, :required => true
|
120
|
-
end
|
121
|
-
|
122
|
-
endpoint 'http://api.flickr.com/services/rest/' do
|
123
|
-
defaults do
|
124
|
-
parameter :method, :required => true
|
125
|
-
end
|
126
|
-
|
127
|
-
action :search do
|
128
|
-
set :method, 'flickr.photos.search'
|
129
|
-
parameter :per_page, :default => 5
|
130
|
-
parameter :tags
|
131
|
-
|
132
|
-
parser :rsp do
|
133
|
-
attribute :stat, :as => :status
|
134
|
-
|
135
|
-
element :photos do
|
136
|
-
attribute :page
|
137
|
-
attribute :pages
|
138
|
-
attribute :perpage, :as => :per_page
|
139
|
-
attribute :total
|
140
|
-
|
141
|
-
elements :photo do
|
142
|
-
attribute :id
|
143
|
-
attribute :owner
|
144
|
-
attribute :secret
|
145
|
-
attribute :server
|
146
|
-
attribute :farm
|
147
|
-
attribute :title
|
148
|
-
attribute :ispublic, :as => :is_public
|
149
|
-
attribute :isfriend, :as => :is_friend
|
150
|
-
attribute :isfamily, :as => :is_family
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
dhe parsing is performed by the Relief gem, so you can find out more about the
|
159
|
-
syntax in its own documentation.
|
160
|
-
|
161
|
-
|
162
|
-
=== Making a Call
|
163
|
-
|
164
|
-
Now, we're able to create a new instance of our service with the API key set.
|
165
|
-
|
166
|
-
flickr = Flickr.new(:api_key => FLICKR_API_KEY)
|
167
|
-
|
168
|
-
We can now use this object to search for photos by tag.
|
169
|
-
|
170
|
-
flickr.search(:tags => 'cucumbers,lemons')
|
171
|
-
|
172
|
-
This will return a Ruby response hash.
|
173
|
-
|
174
|
-
{
|
175
|
-
:status => "ok",
|
176
|
-
:photos => {
|
177
|
-
:page => "1",
|
178
|
-
:pages => "3947",
|
179
|
-
:per_page => "5",
|
180
|
-
:total => "19733",
|
181
|
-
:photo => [
|
182
|
-
{ :is_public => "1", :secret => "3c196485d2", :server => "3182", :is_friend => "0", :farm => "4", :title => "lemons", :is_family => "0", :id => "3509955709", :owner => "37013676@N06" },
|
183
|
-
{ :is_public => "1", :secret => "44f1306a63", :server => "3326", :is_friend => "0", :farm => "4", :title => "Peeto", :is_family => "0", :id => "3509461859", :owner => "13217824@N04" },
|
184
|
-
{ :is_public => "1", :secret => "dce53bce7f", :server => "3364", :is_friend => "0", :farm => "4", :title => "Peeto Above", :is_family => "0", :id => "3509459585", :owner => "13217824@N04" },
|
185
|
-
{ :is_public => "1", :secret => "12f9ba167c", :server => "3632", :is_friend => "0", :farm => "4", :title => "Lemonaid", :is_family => "0", :id => "3509415752", :owner => "35666391@N03" },
|
186
|
-
{ :is_public => "1", :secret => "8caac1ff46", :server => "3320", :is_friend => "0", :farm => "4", :title => "Gardening 365 (Day 8)", :is_family => "0", :id => "3509251322", :owner => "21778017@N06" }
|
187
|
-
]
|
188
|
-
}
|
189
|
-
}
|
190
|
-
|
191
|
-
|
192
|
-
== Copyright
|
193
|
-
|
194
|
-
Copyright (c) 2007-2009 Tyler Hunt. See LICENSE for details.
|
data/VERSION.yml
DELETED
data/lib/relax/action.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
module Relax
|
2
|
-
class Action
|
3
|
-
include Contextable
|
4
|
-
|
5
|
-
attr_reader :name
|
6
|
-
|
7
|
-
def initialize(endpoint, name, options, &block)
|
8
|
-
@endpoint = endpoint
|
9
|
-
@name = name
|
10
|
-
@options = options
|
11
|
-
|
12
|
-
extend_context(endpoint)
|
13
|
-
parse_url_parameters
|
14
|
-
context.evaluate(&block) if block_given?
|
15
|
-
end
|
16
|
-
|
17
|
-
def execute(values, options, *args)
|
18
|
-
args.unshift(values) if values
|
19
|
-
instance = Instance.new(options, *args)
|
20
|
-
response = performer(instance, options).perform
|
21
|
-
context.parse(response)
|
22
|
-
end
|
23
|
-
|
24
|
-
def method
|
25
|
-
@options[:method] || :get
|
26
|
-
end
|
27
|
-
private :method
|
28
|
-
|
29
|
-
def url
|
30
|
-
[@endpoint.url, @options[:url]].join
|
31
|
-
end
|
32
|
-
private :url
|
33
|
-
|
34
|
-
def performer(instance, options)
|
35
|
-
values = instance.values(context)
|
36
|
-
Performer.new(method, url, values, options)
|
37
|
-
end
|
38
|
-
private :performer
|
39
|
-
|
40
|
-
def parse_url_parameters
|
41
|
-
url.scan(/(?:\:)([a-z_]+)/).flatten.each do |name|
|
42
|
-
defaults do
|
43
|
-
parameter name.to_sym, :required => true
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
private :parse_url_parameters
|
48
|
-
end
|
49
|
-
end
|
data/lib/relax/context.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
module Relax
|
2
|
-
class Context
|
3
|
-
attr_reader :parameters
|
4
|
-
|
5
|
-
def initialize(parameters=[]) # :nodoc:
|
6
|
-
@parameters = parameters
|
7
|
-
end
|
8
|
-
|
9
|
-
def evaluate(&block) # :nodoc:
|
10
|
-
instance_eval(&block)
|
11
|
-
end
|
12
|
-
|
13
|
-
def parameter(name, options={})
|
14
|
-
unless @parameters.find { |parameter| parameter.named?(name) }
|
15
|
-
@parameters << Parameter.new(name, options)
|
16
|
-
else
|
17
|
-
raise ArgumentError.new("Duplicate parameter '#{name}'.")
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def set(name, value)
|
22
|
-
if parameter = @parameters.find { |parameter| parameter.named?(name) }
|
23
|
-
parameter.value = value
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def parser(root, options={}, &block) # :nodoc:
|
28
|
-
@parser ||= begin
|
29
|
-
if root.kind_of?(Class)
|
30
|
-
root.new(options, &block)
|
31
|
-
else
|
32
|
-
Relief::Parser.new(root, options, &block)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def parse(response) # :nodoc:
|
38
|
-
@parser.parse(response)
|
39
|
-
end
|
40
|
-
|
41
|
-
def clone # :nodoc:
|
42
|
-
cloned_parameters = @parameters.collect { |parameter| parameter.clone }
|
43
|
-
self.class.new(cloned_parameters)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
data/lib/relax/contextable.rb
DELETED
data/lib/relax/endpoint.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Relax
|
2
|
-
class Endpoint
|
3
|
-
include Contextable
|
4
|
-
|
5
|
-
attr_reader :url
|
6
|
-
|
7
|
-
def initialize(service, url, options, &block)
|
8
|
-
@service = service
|
9
|
-
@url = url
|
10
|
-
@options = options
|
11
|
-
|
12
|
-
extend_context(service)
|
13
|
-
instance_eval(&block)
|
14
|
-
end
|
15
|
-
|
16
|
-
def action(name, options={}, &block)
|
17
|
-
action = Action.new(self, name, options, &block)
|
18
|
-
@service.register_action(action)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/relax/instance.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Relax
|
2
|
-
class Instance # :nodoc:
|
3
|
-
def initialize(options, *args)
|
4
|
-
@options = options
|
5
|
-
|
6
|
-
@values = args.inject({}) do |values, arg|
|
7
|
-
arg.is_a?(Hash) ? values.merge(arg) : values
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def values(context)
|
12
|
-
context.parameters.inject({}) do |values, parameter|
|
13
|
-
name = parameter.options[:as] || parameter.name
|
14
|
-
|
15
|
-
if (value = @values[name] || parameter.value) || @options[:include_blank_values]
|
16
|
-
values[parameter.name] = value
|
17
|
-
elsif parameter.required?
|
18
|
-
raise ArgumentError.new("Missing value for '#{name}'.")
|
19
|
-
end
|
20
|
-
|
21
|
-
values
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/relax/parameter.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Relax
|
2
|
-
class Parameter
|
3
|
-
attr_reader :name, :options
|
4
|
-
attr_writer :value
|
5
|
-
|
6
|
-
def initialize(name, options={})
|
7
|
-
@name = name
|
8
|
-
@options = options
|
9
|
-
end
|
10
|
-
|
11
|
-
def named?(name)
|
12
|
-
name == (@options[:as] || @name)
|
13
|
-
end
|
14
|
-
|
15
|
-
def value
|
16
|
-
@value || @options[:default]
|
17
|
-
end
|
18
|
-
|
19
|
-
def required?
|
20
|
-
@options[:required]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/relax/performer.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
module Relax
|
2
|
-
class Performer
|
3
|
-
def initialize(method, url, values, options={})
|
4
|
-
@method = method
|
5
|
-
@url = url
|
6
|
-
@values = values
|
7
|
-
@logger = options.delete(:logger)
|
8
|
-
@credentials = options.delete(:credentials)
|
9
|
-
@proxy = options.delete(:proxy)
|
10
|
-
@options = options
|
11
|
-
|
12
|
-
parse_url_tokens
|
13
|
-
end
|
14
|
-
|
15
|
-
def perform
|
16
|
-
RestClient.proxy = @proxy if @proxy
|
17
|
-
|
18
|
-
case @method
|
19
|
-
when :delete, :get, :head
|
20
|
-
@logger.info "#{@method.to_s.upcase} #{url}" if @logger
|
21
|
-
RestClient.send(@method, url)
|
22
|
-
when :post, :put
|
23
|
-
@logger.info "#{@method.to_s.upcase} #{url}\n#{query}" if @logger
|
24
|
-
RestClient.send(@method, url, query)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def url
|
29
|
-
url = @url.gsub(/\:[a-z_]+/) do |name|
|
30
|
-
@url_values[name[1..-1].to_sym]
|
31
|
-
end
|
32
|
-
|
33
|
-
uri = URI.parse(url)
|
34
|
-
uri.query = query unless query.nil? || query.empty?
|
35
|
-
uri.userinfo = @credentials.join(':') if @credentials
|
36
|
-
uri.to_s
|
37
|
-
end
|
38
|
-
private :url
|
39
|
-
|
40
|
-
def query
|
41
|
-
@values.collect do |name, value|
|
42
|
-
"#{name}=#{value}" if value || @options[:include_blank_values]
|
43
|
-
end.compact.join('&')
|
44
|
-
end
|
45
|
-
private :query
|
46
|
-
|
47
|
-
def parse_url_tokens
|
48
|
-
tokens = @url.scan(/(?:\:)([a-z_]+)/).flatten
|
49
|
-
|
50
|
-
@url_values = tokens.inject({}) do |values, name|
|
51
|
-
name = name.to_sym
|
52
|
-
values[name] = @values.delete(name) if @values.key?(name)
|
53
|
-
values
|
54
|
-
end
|
55
|
-
end
|
56
|
-
private :parse_url_tokens
|
57
|
-
end
|
58
|
-
end
|