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/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
@@ -1,4 +0,0 @@
1
- ---
2
- :major: 0
3
- :minor: 1
4
- :patch: 3
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
@@ -1,15 +0,0 @@
1
- module Relax
2
- module Contextable # :nodoc:
3
- def context
4
- @context ||= Context.new
5
- end
6
-
7
- def extend_context(base)
8
- @context = base.context.clone
9
- end
10
-
11
- def defaults(&block)
12
- context.evaluate(&block)
13
- end
14
- end
15
- end
@@ -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
@@ -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
@@ -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
@@ -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